diff --git a/crowbar/.loadpath b/crowbar/.loadpath new file mode 100644 index 00000000000..c4415b3d019 --- /dev/null +++ b/crowbar/.loadpath @@ -0,0 +1,5 @@ + + + + + diff --git a/crowbar/.project b/crowbar/.project new file mode 100644 index 00000000000..e79fd0467c7 --- /dev/null +++ b/crowbar/.project @@ -0,0 +1,18 @@ + + + crowbar + + + + + + org.rubypeople.rdt.core.rubybuilder + + + + + + org.rubypeople.rdt.core.rubynature + org.radrails.rails.core.railsnature + + diff --git a/crowbar/CHANGE-LOG b/crowbar/CHANGE-LOG new file mode 100644 index 00000000000..ceb80c7a653 --- /dev/null +++ b/crowbar/CHANGE-LOG @@ -0,0 +1,4 @@ + + +--- Initial Version ---- + diff --git a/crowbar/README.build b/crowbar/README.build new file mode 100644 index 00000000000..d30589e37d9 --- /dev/null +++ b/crowbar/README.build @@ -0,0 +1,142 @@ +This file documents how to build the Crowbar installation DVD image. + +Prerequisites: + * An unfiltered Internet connection and a decent amount of bandwidth. + The first time you try to build Crowbar it will need to download + about 1.5 gigs worth of data from the official Ubuntu apt repositories, + the Opscode chef repository, the official Openstack 2011.2 PPA, and + an official Ubuntu CD image mirror. + * bash version 4 or higher + build_crowbar.sh uses associative arrays, which bash got in version 4. + * mkisofs + The end product of the build script is an ISO image that will be used + to bootstrap the Crowbar admin node. + * debootstrap + The build process needs to download all the .debs and gems that + Crowbar requires, and we don't want to inadvertently mess up the build + machine when we do that. All extra packages are downloaded into a + chrooted minimal Ubuntu intall, and we use debootstrap to enable that. + * Sudo to root privileges for the following commands: + * /bin/mount, /bin/umount + We have to be able to mount and umount the Ubuntu .iso image, as well as + a tmpfs for debootstrap, and we have to be able to bind mount + /dev, /dev/pts, /proc, and /sys into the debootstrap chroot environment. + * /usr/sbin/debootstrap + debootstrap requires root privileges to run. + * /bin/cp + We need to copy things into and out of the debootstrap environment to + ensure it downloads and caches the right packages. + * /usr/sbin/chroot + All our package caching is done in a chroot environment, and chroot + requires root permissions to run. + + If you want to allow build_crowbar to run the above commands as root + without having to enter a password each time, the build_crowbar.sh + includes a sample line you can fix up and add to /etc/sudoers. + * A working Sledgehammer tftpboot image. + Sledgehammer is a minimal CentOS 5.6 pxe bootable image that we use + to perform hardware discovery and configuration for devices that do not + have low-level management tools for Ubuntu. You can find information on + how to build Sledgehamemr by reading its HOWTO.Non.Redhat file. We do + not do fully automated builds of Sledgehammer due to a lack of a CentOS + equivalent of debootstrap. + +Usage: + * Run build_crowbar.sh. + The first time it is run, it will download and cache all the files it + needs from the official Ubuntu, Chef, and Openstack repositories. It will + then build the Crowbar installation image, save it to the current + working directory, and print out a message saying where it saved the image. + On subsequent runs it will run with the files it cached from the first + run, unless update-cache is passed as a parameter to the script. + +Customization: + + build_crowbar.sh has several different parameters you can tune, either from + $HOME/.build-crowbar.conf (for developer use), or from build-crowbar.conf + in the current directory (for automated builds). + + Here are the parameters you can change through the above configuration files: + * DEBUG + If DEBUG is set to anything, build_crowbar will run in debug mode, and will + print a transcript of everything it is doing to standard error. + * CACHE_DIR + This is the default location where build_crowbar.sh will keep the files + it caches, along with the temporary directories used to mount the + ISO image, the debootstrap chroot, and the directory we perform the build + in. It defaults to $HOME/.crowbar-build-cache. + * ISO_LIBRARY + This is the default location where the Ubuntu .iso is stored. It defaults + to $CACHE_DIR/iso + * ISO_DEST + This is the location that we will save the Crowbar install iamge to. + It defaults to the current directory. + * IMAGE_DIR + This is the location that we will mount isos in. + It defaults to $CACHE_DIR/image + * BUILD_DIR + This is the directory we will stage the Crowbar build into. + It defaults to $CACHE_DIR/build + * UBUNTU_VERSION + This is the numeric version of Ubuntu we are building Crowbar with. + Right now we base the build on 10.10 + * UBUNTU_CODENAME + This is the one-word codename of $UBUNTU_VERSION. Weird things will + happen if it is not kept in sync with $UBUNTU_VERSION + * PPAS + This is an array of PPA archives to look in to gather packages from. + This defaults to ("openstack-release/2011.2"), which is the official + PPA for the Cactus release of Openstack. + * UBUNTU_ISO_MIRROR + This is the base URL for the mirror we will try to download the + Ubuntu server ISO from. It defaults to "http://mirror.anl.gov/pub". + * SLEDGEHAMMER_PXE_DIR + This points to the location we expect to find the unpacked Sledgehammer + PXE boot archive. It defaults to $CACHE_DIR/tftpboot + * VERSION + The default version of Crowbar. Defaults to dev. + * OPENSTACK_ISO + The name of the ISO that build_crowbar.sh generates. + Defaults to openstack-$VERSION.iso + * CROWBAR_DIR + The directory that the Crowbar source is cheched out to. + Defaults to the directory that build_crowbar.sh is in. + * SLEDGEHAMMER_DIR + The directory that the Sledgehammer source is checked out to. + Defaults to $CROWBAR_DIR/../sledgehammer + * VCS_CLEAN_CMD + This is the command that build_crowbar.sh will run to clean the tree before + staging the Crowbar build. + Defaults to 'git clean -f -x -d' + * DEBS + This is an array of additional packages that build_crowbar.sh will try + to populate the .deb cache with. It is empty by default. + * GEMS + This is an array of additional Ruby gems that build_crowbar will try to + populate its .gem cache with. It is empty by default. + * AMIS + This is an array of urls for AMI images that you want to prepopulate + Crowbar with. By default it contains the URL for the official release + AMI of Ubuntu 11.04 x86_64 server. + * UBUNTU_CHROOT + This is the directory that we will mount a tmpfs on when we debootstrap + a minimal Ubuntu install to prepopulate the deb and gem caches. + It defaults to $CACHE_DIR/$UBUNTU_CODENAME.chroot + * DEB_CACHE + This is the directory that will hold our .deb cache. + It defaults to $CACHE_DIR/$UBUNTU_CODENAME/debs + * GEM_CACHE + This is the directory that will hold our gem cache. + It defaults to $CACHE_DIR/gems + * AMI_CACHE + This is the directory that will hold our AMI cache. + It defaults to $CACHE_DIR/amis + * UBUNTU_ISO + This is the full name of the Ubuntu ISO we will use as the base of the + Crowbar installation image. + Defaults to "ubuntu-$UBUNTU_VERSION-server-amd64.iso", which will + base us on the amd64 server spin of Ubuntu. + * UBUNTU_DIR + This is the directory we will mount $UBUNTU_ISO on when we extract files + from it. + Defaults to $IMAGE_DIR/$UBUNTU_ISO minus the .iso at the end. diff --git a/crowbar/build_crowbar.sh b/crowbar/build_crowbar.sh new file mode 100755 index 00000000000..da9afe8f7f3 --- /dev/null +++ b/crowbar/build_crowbar.sh @@ -0,0 +1,453 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +# This script expects to be able to run certian commands as root. +# Either run it as a user who can sudo to root, or give the user +# you are running it as the following sudo rights: +# crowbar-tester ALL = NOPASSWD: /bin/mount, /bin/umount, /usr/sbin/debootstrap, /bin/cp, /usr/sbin/chroot + +# When running this script for the first time, it will automatically create a +# cache directory and try to populate it with all the build dependencies. +# After that, if you need to pull in new dependencies, you will need to +# call the script with the update-cache parameter. + +[[ $DEBUG ]] && { + set -x + export PS4='(${nodename:-none})${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): ' +} + +export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" + +# Location for caches that should not be erased between runs +CACHE_DIR="$HOME/.crowbar-build-cache" + +# Location to store .iso images +ISO_LIBRARY="$CACHE_DIR/iso" +ISO_DEST="$PWD" + +# Location that holds temporary mount images of our.isos +# if we need to extract them onto our target image. +IMAGE_DIR="$CACHE_DIR/image" + +# Location we will stage the new openstack iso at. +BUILD_DIR="$CACHE_DIR/build" + +# Version of Ubuntu we are building openstack on to. +UBUNTU_VERSION=10.10 +UBUNTU_CODENAME=maverick +PPAS=("openstack-release/2011.2") + +# Server to download the mirror from if we need to. +UBUNTU_ISO_MIRROR="http://mirror.anl.gov/pub" + +# Directory that holds our Sledgehammer PXE tree. +SLEDGEHAMMER_PXE_DIR="$CACHE_DIR/tftpboot" + +# Version for ISO +VERSION="dev" + +# Name of the openstack iso we will build +OPENSTACK_ISO="openstack-${VERSION}.iso" + +# Location of the Crowbar checkout we are building from. +CROWBAR_DIR="${0%/*}" +SLEDGEHAMMER_DIR="${CROWBAR_DIR}/../sledgehammer" +VCS_CLEAN_CMD='git clean -f -x -d' + +# Arrays holding the additional debs, gems, and AMI images we will populate +# Crowbar with. +DEBS=() +GEMS=() +AMIS=("http://uec-images.ubuntu.com/releases/11.04/release/ubuntu-11.04-server-uec-amd64.tar.gz") + + +die() { shift; echo "$(date '+%F %T %z'): $*" >&2; exit 1; } +debug() { echo "$(date '+%F %T %z'): $*" >&2; } +clean_dirs() { + local d='' + for d in "$@"; do + ( mkdir -p "$d" + cd "$d" + chmod -R u+w . + rm -rf * ) + done +} + +update_caches() { + # Hold a list of directories we will need to umount + TO_UMOUNT=() + + # Make sure our cache directories exist. + mkdir -p "$DEB_CACHE" + mkdir -p "$GEM_CACHE" + + # A little helper function for doing bind mounts. + bind_mount() { + TO_UMOUNT=("${TO_UMOUNT[@]}" "$2") + [[ -d $2 ]] || mkdir -p "$2" + grep -q "$2" /proc/self/mounts || sudo mount --bind "$1" "$2" + } + + # A little helper for running commands in the chroot. + in_chroot() { sudo -H chroot "$UBUNTU_CHROOT" "$@"; } + + # second, debootstrap a minimal install of our target version of + # Ubuntu to ensure that we don't interfere with the host's package cache. + debug "Making package-fetching chroot" + mkdir -p "$UBUNTU_CHROOT" + sudo mount -t tmpfs -o size=1G none "$UBUNTU_CHROOT" + sudo debootstrap "$UBUNTU_CODENAME" "$UBUNTU_CHROOT" \ + "file://$BUILD_DIR" || \ + die 1 "Could not bootstrap our scratch target!" + # mount some important directories for the chroot + for d in proc sys dev dev/pts; do + bind_mount "/$d" "$UBUNTU_CHROOT/$d" + done + # make sure the chroot can resolve hostnames + sudo cp /etc/resolv.conf "$UBUNTU_CHROOT/etc/resolv.conf" + + # Make sure we are using a correctly prepopulated sources.list. + sudo cp "$BUILD_DIR/extra/sources.list" \ + "$UBUNTU_CHROOT/etc/apt/sources.list" + + # if we have deb caches, copy them back in to save time on the downloads. + sudo cp -a "$DEB_CACHE/." "$UBUNTU_CHROOT/var/cache/apt/archives/." + + debug "Fetching needed packages" + # update, add infrastructure for adding PPAs, + # add additional PPAs, and update again. + in_chroot /usr/bin/apt-get -y --force-yes --allow-unauthenticated update + in_chroot /usr/bin/apt-get -y --force-yes --allow-unauthenticated install \ + python-software-properties + for ppa in "${PPAS[@]}"; do + in_chroot apt-add-repository "ppa:$ppa" + done + # Get the key for the Opscode repo we are grabbing Chef bits from. + wget -qO - http://apt.opscode.com/packages@opscode.com.gpg.key | \ + in_chroot /usr/bin/apt-key add - + in_chroot /usr/bin/apt-get -y --force-yes --allow-unauthenticated update + + # Download all the packages apt thinks we will need. + in_chroot /usr/bin/apt-get -y --force-yes \ + --allow-unauthenticated --download-only install "${DEBS[@]}" + # actually install ruby1.8-dev and gem and their deps. + in_chroot /usr/bin/apt-get -y --force-yes \ + --allow-unauthenticated install ruby1.8-dev rubygems1.8 build-essential + # install the gems we will need and all their dependencies + # We will get some build failures, but at this point we don't care because + # we are just caching the gems for the real install. + debug "Fetching Gems" + echo "There may be build failures here, we can safely ignore them." + gem_re='([^0-9].*)-([0-9].*)' + for gem in "${GEMS[@]}"; do + if [[ $gem =~ $gem_re ]]; then + echo "${BASH_REMATCH[*]}" + gemname="${BASH_REMATCH[1]}" + gemver="${BASH_REMATCH[2]}" + else + gemname="$gem" + gemver='' + fi + gemopts=(install --no-ri --no-rdoc) + [[ $gemver ]] && gemopts+=(--version "= ${gemver}") + in_chroot /usr/bin/gem "${gemopts[@]}" "$gemname" + done + debug "Saving downloaded packages" + # Save our updated gems and debs in the cache for later. + cp -a "$UBUNTU_CHROOT/var/cache/apt/archives/." "$DEB_CACHE/." + cp -a "$UBUNTU_CHROOT/var/lib/gems/1.8/cache/." "$GEM_CACHE/." + sync + + debug "Cleaning up mounts" + # umount all the stuff we have mounted for the chroot. + while grep -q "$UBUNTU_CHROOT" /proc/self/mounts; do + for m in "${TO_UMOUNT[@]}"; do sudo umount "$m"; sleep 1; done + sudo umount "$UBUNTU_CHROOT" + done +} + +copy_debs() { + # $1 = pool directory to build initial list of excludes against + # $2 = directory to copy from + # $3 = directory to copy to. + + # First, a couple of hashes to hold deb => revision values + declare -A deb_pool + declare -A dest_pool + + local debname='' + local debarr=() + local debs_to_copy=() + + + # Scan through our pool to find debs we can easily omit. + while read debname; do + [[ -f $debname && $debname = *.deb ]] || continue + debname="${debname##*/}" + debarr=(${debname//_/ }) # split into (name version arch) + deb_pool["${debarr[0]}"]="${debarr[1]}" + done < <(find "$1" -name '*.deb') + + ( + cd "$2" + for deb in *; do + [[ -f $deb && $deb = *.deb ]] || continue + debname="${deb##*/}" # don't care about the source path + debname="${debname%_*.deb}" # don't care about the arch + debver="${debname#*_}" + debname="${debname%_*}" + # First, have we already copied another version of this + # deb? If so, decide whether to copy it or not. + if [[ ${dest_pool["$debname"]} ]]; then + # We have seen it. If the version we already copied + # is older than this one, queue this for copying instead. + # This relies on * expansion returning names in order. + if [[ ${dest_pool["$debname"]} < $debver ]]; then + debug "Omitting ${debname}_${dest_pool[$debname]} in favor of ${debname}_${debver}" + debs_to_copy[$((${#debs_to_copy[@]} - 1))]="$deb" + dest_pool["$debname"]="$debver" + fi + # Second check and see if it is already inthe install pool. + # If it is and it is the same or lesser version, don't copy it + elif [[ ${deb_pool["$debname"]} ]]; then + if [[ ${deb_pool["$debname"]} < $debver ]]; then + debs_to_copy+=("$deb") + else + debug "${debname}_${debver} in CD pool, omitting" + fi + else + # It is not already in deb_pool or dest_pool, copy it. + debs_to_copy+=("$deb") + debug "Will copy ${debname}_${debver}" + fi + done + # Now, we have a list of debs to copy, so do it. + mkdir -p "$3" + cp -t "$3" "${debs_to_copy[@]}" + ) +} + +maybe_update_cache() { + local pkgfile deb gem pkg_type rest need_update _pwd + debug "Processing package lists" + # Zero out our sources.list + > "$BUILD_DIR/extra/sources.list" + # Download and stash any extra files we may need + # First, build our list of repos, ppas, debs, and gems + for pkgfile in "$BUILD_DIR/extra/packages/"*.list; do + [[ -f $pkgfile ]] || continue + while read pkg_type rest; do + case $pkg_type in + repository) + echo "${rest%%#*}" >> "$BUILD_DIR/extra/sources.list";; + ppas) PPAS+=(${rest%%#*});; + debs) DEBS+=(${rest%%#*});; + gems) GEMS+=(${rest%%#*});; + esac + done <"$pkgfile" + done + + _pwd=$PWD + cd "$DEB_CACHE" + # second, verify that the debs we need are in the cache. + for deb in "${DEBS[@]}"; do + [[ $(echo "$deb"*.deb) != "$deb*.deb" ]] || { + need_update=true + break + } + done + + cd "$GEM_CACHE" + # third, verify that the gems we need are in the cache + for gem in "${GEMS[@]}"; do + [[ $(echo "$gem"*.gem) != "$gem*.gem" ]] || { + need_update=true + break + } + done + cd "$_pwd" + + if [[ $need_update = true || \ + ( ! -d $DEB_CACHE ) || $* =~ update-cache ]]; then + update_caches + else + return 0 + fi +} + +for cmd in sudo chroot debootstrap mkisofs; do + which "$cmd" &>/dev/null || \ + die 1 "Please install $cmd before trying to build Crowbar." +done + +{ + # Make sure only one instance of the ISO build runs at a time. + # Otherwise you can easily end up with a corrupted image. + flock 65 + + # Source our config file if we have one + [[ -f $HOME/.build-crowbar.conf ]] && \ + . "$HOME/.build-crowbar.conf" + + # Look for a local one. + [[ -f build-crowbar.conf ]] && \ + . "build-crowbar.conf" + + # Finalize where we expect to find our caches and out chroot. + # If they were set in one of the conf files, don't touch them. + + # The directory we perform a minimal install of Ubuntu into if we need + # to refresh our gem or deb caches + [[ $UBUNTU_CHROOT ]] || UBUNTU_CHROOT="$CACHE_DIR/$UBUNTU_CODENAME.chroot" + + # Directories where we cache our debs, gems, and ami files + [[ $DEB_CACHE ]] || DEB_CACHE="$CACHE_DIR/$UBUNTU_CODENAME/debs" + [[ $GEM_CACHE ]] || GEM_CACHE="$CACHE_DIR/gems" + [[ $AMI_CACHE ]] || AMI_CACHE="$CACHE_DIR/amis" + + # The name of the Ubuntu iso we are using as a base. + [[ $UBUNTU_ISO ]] || UBUNTU_ISO="ubuntu-$UBUNTU_VERSION-server-amd64.iso" + + # directory we will mount the Ubuntu .iso on to extract packages. + [[ $UBUNTU_DIR ]] || UBUNTU_DIR="$IMAGE_DIR/${UBUNTU_ISO%.iso}" + + + # Make any directories we don't already have + for d in "$ISO_LIBRARY" "$ISO_DEST" "$IMAGE_DIR" "$BUILD_DIR" "$AMI_CACHE" \ + "$SLEDGEHAMMER_PXE_DIR" "$UBUNTU_CHROOT"; do + mkdir -p "$d" + done + + # Make sure Sledgehammer has already been built and pre-staged. + if ! [[ -f $SLEDGEHAMMER_DIR/bin/sledgehammer-tftpboot.tar.gz || \ + -f $SLEDGEHAMMER_PXE_DIR/initrd0.img ]]; then + echo "Slegehammer TFTP image missing!" + echo "Please build Sledgehammer from $SLEDGEHAMMER_DIR before buildin gCrowbar." + exit 1 + fi + + # make sure we have the AMIs we want + for ami in "${AMIS[@]}"; do + [[ -f $AMI_CACHE/${ami##*/} ]] && continue + echo "$(date '+%F %T %z'): Downloading and caching $ami" + curl -o "$AMI_CACHE/${ami##*/}" "$ami" || \ + die 1 "Could not download $ami" + done + + # Try and download our ISO if we don't already have it + [[ -f $ISO_LIBRARY/$UBUNTU_ISO ]] || { + echo "$(date '+%F %T %z'): Downloading and caching $UBUNTU_ISO" + curl -o "$ISO_LIBRARY/$UBUNTU_ISO" \ + "$UBUNTU_ISO_MIRROR/ubuntu-iso/CDs/$UBUNTU_VERSION/$UBUNTU_ISO" || \ + die 1 "Missing our Ubuntu source image" + } + + # Start with a clean slate. + clean_dirs "$UBUNTU_DIR" "$BUILD_DIR" + + (cd "$CROWBAR_DIR"; $VCS_CLEAN_CMD) + # Copy everything off the Ubuntu ISO to our build directory + debug "Copying Ubuntu off $UBUNTU_ISO" + sudo mount -t iso9660 -o loop "$ISO_LIBRARY/$UBUNTU_ISO" "$UBUNTU_DIR" || \ + die "Could not mount $UBUNTU_ISO" + cp -rT "$UBUNTU_DIR" "$BUILD_DIR" + sudo umount -d "$UBUNTU_DIR" + + # Make everything writable again. + chmod -R u+w "$BUILD_DIR" + + # Make additional directories we will need. + for d in discovery extra ami updates ; do + mkdir -p "$BUILD_DIR/$d" + done + + # Copy over the Crowbar bits and their prerequisites + debug "Staging extra Crowbar bits" + cp -r "$CROWBAR_DIR/ubuntu-$UBUNTU_VERSION-extra"/* "$BUILD_DIR/extra" + cp -r "$CROWBAR_DIR/change-image"/* "$BUILD_DIR" + + # If we were asked to update our cache, do it. + maybe_update_cache "$@" + + # Copy our extra debs, gems, and amis over + debug "Copying debs, gems, and amis" + copy_debs "$BUILD_DIR/pool" "$DEB_CACHE" "$BUILD_DIR/extra/debs" + cp -r "$GEM_CACHE" "$BUILD_DIR/extra" + cp -r "$AMI_CACHE/." "$BUILD_DIR/ami/." + + # Make our new packages repository. + ( cd "$BUILD_DIR/extra" + debug "Recreating Packages.gz" + dpkg-scanpackages debs /dev/null 2>/dev/null |gzip -9 >Packages.gz) + # Store off the version + echo "$VERSION" >> "$BUILD_DIR/dell/Version" + + # Fix up the initrd + ( cd "$CROWBAR_DIR/initrd" + debug "Fixing up initrd" + [[ -d scratch ]] && rm -rf scratch + mkdir scratch + # Grab _all_ the nic drivers. We probably don't need them, + # but a little paranoia never hurt anyone. + ( cd scratch; + debug "Adding all nic drivers" + for udeb in "$BUILD_DIR/pool/main/l/linux/"nic-*-generic-*.udeb; do + ar x "$udeb" + tar xzf data.tar.gz + rm -rf debian-binary *.tar.gz + done + # Make sure installing off a USB connected DVD will work + debug "Adding USB connected DVD support" + mkdir -p var/lib/dpkg/info + cp ../cdrom-detect.postinst var/lib/dpkg/info + # Append our new gzipped CPIO archive onto the old one. + find . |cpio --create --format=newc --owner root:root 2>/dev/null | \ + gzip -9 >> "$BUILD_DIR/install/initrd.gz" ) + rm -rf scratch ) + + # Copy over the Sledgehammer bits + debug "Copying over Sledgehammer bits" + for d in "$CROWBAR_DIR/"updates*; do + [[ -d $d ]] || continue + cp -r "$d"/* "$BUILD_DIR/updates" + done + # If we need to copy over a new Sledgehammer image, do so. + if [[ $SLEDGEHAMMER_DIR/bin/sledgehammer-tftpboot.tar.gz -nt \ + $SLEDGEHAMMER_PXE_DIR/initrd0.img ]]; then + ( cd $SLEDGEHAMMER_PXE_DIR + debug "Extracting new Sledgehammer TFTP boot image" + rm -rf . + cd .. + tar xzf "$SLEDGEHAMMER_DIR/bin/sledgehammer-tftpboot.tar.gz" + rm -f "$SLEDGEHAMMER_DIR/bin/sledgehammer-tftpboot.tar.gz" + ) + fi + cp -a "$SLEDGEHAMMER_PXE_DIR"/* "$BUILD_DIR/discovery" + + # Make our image + debug "Creating new ISO" + ( cd "$BUILD_DIR" + find -name '.svn' -type d -exec rm -rf '{}' ';' 2>/dev/null >/dev/null + mkdir -p $ISO_DEST + mkisofs -r -V "Crowbar $VERSION DVD" -cache-inodes -J -l -quiet \ + -b isolinux/isolinux.bin -c isolinux/boot.cat \ + -no-emul-boot --boot-load-size 4 -boot-info-table \ + -o "$ISO_DEST/$OPENSTACK_ISO" "$BUILD_DIR" ) || \ + die 1 "There was a problem building our ISO." + echo "$(date '+%F %T %z'): Finshed. Image at $ISO_DEST/$OPENSTACK_ISO" +} 65> /tmp/.build_crowbar.lock diff --git a/crowbar/change-image/dell/PC6200v3.2.0.7.stk b/crowbar/change-image/dell/PC6200v3.2.0.7.stk new file mode 100644 index 00000000000..9914bcd17a0 Binary files /dev/null and b/crowbar/change-image/dell/PC6200v3.2.0.7.stk differ diff --git a/crowbar/change-image/dell/apt.conf b/crowbar/change-image/dell/apt.conf new file mode 100644 index 00000000000..8602f01c0d5 --- /dev/null +++ b/crowbar/change-image/dell/apt.conf @@ -0,0 +1 @@ +APT::Get::AllowUnauthenticated 1 ; diff --git a/crowbar/change-image/dell/barclamp_lib.rb b/crowbar/change-image/dell/barclamp_lib.rb new file mode 100755 index 00000000000..675808a6c0f --- /dev/null +++ b/crowbar/change-image/dell/barclamp_lib.rb @@ -0,0 +1,444 @@ + +require 'rubygems' +require 'net/http' +require 'net/http/digest_auth' +require 'uri' +require 'json' +require 'getoptlong' + +@debug = false +@hostname = ENV["CROWBAR_IP"] +# DO NOT CHANGE THE NEXT 2 LINES +# gather_cli replies on the exact format they are in. +@hostname = "127.0.0.1" unless @hostname +@port = 3000 +@headers = { + "Accept" => "application/json", + "Content-Type" => "application/json" +} +@data = "" +@allow_zero_args = false +@timeout = 500 +@key = ENV["CROWBAR_KEY"] +if @key + @username=@key.split(':',2)[0] + @password=@key.split(':',2)[1] +end + +# +# Parsing options can be added by adding to this list before calling opt_parse +# +@options = [ + [ [ '--help', '-h', GetoptLong::NO_ARGUMENT ], "--help or -h - help" ], + [ [ '--username', '-U', GetoptLong::REQUIRED_ARGUMENT ], "--username or -U - specifies the username" ], + [ [ '--password', '-P', GetoptLong::REQUIRED_ARGUMENT ], "--password or -P - specifies the password" ], + [ [ '--hostname', '-n', GetoptLong::REQUIRED_ARGUMENT ], "--hostname or -n - specifies the destination server" ], + [ [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], "--port or -p - specifies the destination server port" ], + [ [ '--debug', '-d', GetoptLong::NO_ARGUMENT ], "--debug or -d - turns on debugging information" ], + [ [ '--data', GetoptLong::REQUIRED_ARGUMENT ], "--data - used by create or edit as data (must be in json format)" ], + [ [ '--file', GetoptLong::REQUIRED_ARGUMENT ], "--file - used by create or edit as data when read from a file (must be in json format)" ], + [ [ '--timeout', GetoptLong::REQUIRED_ARGUMENT ], "--timeout - timeout in seconds for read http reads" ] +] + +# +# New commands can be added by adding to this list before calling run_command +# +# Proposal is an example of running sub-commands +# +@proposal_commands = { + "list" => [ "proposal_list", "list - show a list of current proposals" ], + "create" => [ "proposal_create ARGV.shift", "create - create a proposal" ], + "show" => [ "proposal_show ARGV.shift", "show - show a specific proposal" ], + "edit" => [ "proposal_edit ARGV.shift", "edit - edit a new proposal" ], + "delete" => [ "proposal_delete ARGV.shift", "delete - delete a proposal" ], + "commit" => [ "proposal_commit ARGV.shift", "commit - Commit a proposal to active" ], + "dequeue" => [ "proposal_dequeue ARGV.shift", "dequeue - Dequeue a proposal to active" ] +} + +@commands = { + "help" => [ "help", "help - this page" ], + "api_help" => [ "api_help", "crowbar API help - help for this barclamp." ], + "list" => [ "list", "list - show a list of current configs" ], + "show" => [ "show ARGV.shift", "show - show a specific config" ], + "delete" => [ "delete ARGV.shift", "delete - delete a config" ], + "proposal" => [ "run_sub_command(@proposal_commands, ARGV.shift)", "proposal - Proposal sub-commands", @proposal_commands ], + "elements" => [ "elements", "elements - List elements of a #{@barclamp} deploy" ], + "element_node" => [ "element_node ARGV.shift", "element_node - List nodes that could be that element" ], + "transition" => [ "transition(ARGV.shift,ARGV.shift)", "transition - Transition machine named name to state" ] +} + + +def print_commands(cmds, spacer = " ") + cmds.each do |key, command| + puts "#{spacer}#{command[1]}" + print_commands(command[2], " #{spacer}") if command[0] =~ /run_sub_command\(/ + end +end + +def usage (rc) + puts "Usage: crowbar #{@barclamp} [options] " + @options.each do |options| + puts " #{options[1]}" + end + print_commands(@commands) + exit rc +end + +def help + usage 0 +end + +def authenticate(req,uri,data=nil) + uri.user=@username + uri.password=@password + res=nil + Net::HTTP.start(uri.host, uri.port) {|http| + http.read_timeout = @timeout + r = req.new(uri.request_uri,@headers) + r.body = data if data + res = http.request r + puts "DEBUG: (a) return code: #{res.code}" if @debug + puts "DEBUG: (a) return body: #{res.body}" if @debug + puts "DEBUG: (a) return headers: #{res.headers}" if @debug + if res['www-authenticate'] + digest_auth=Net::HTTP::DigestAuth.new + auth=Net::HTTP::DigestAuth.new.auth_header(uri, + res['www-authenticate'], + req::METHOD) + r.add_field 'Authorization', auth + res = http.request r + end + } + res +end + +def get_json(path) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Get,uri) + + puts "DEBUG: (g) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (g) request: #{uri.path}" if @debug + puts "DEBUG: (g) return code: #{res.code}" if @debug + puts "DEBUG: (g) return body: #{res.body}" if @debug + + return [res.body, res.code.to_i ] if res.code.to_i != 200 + + struct = JSON.parse(res.body) + + puts "DEBUG: (g) JSON parse structure = #{struct.inspect}" if @debug + + return [struct, 200] +end + +def post_json(path, data) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Post,uri,data) + + puts "DEBUG: (post) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (post) request: #{uri.path}" if @debug + puts "DEBUG: (post) data: #{@data}" if @debug + puts "DEBUG: (post) return code: #{res.code}" if @debug + puts "DEBUG: (post) return body: #{res.body}" if @debug + + [res.body, res.code.to_i ] +end + +def put_json(path, data) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Put,uri,data) + + puts "DEBUG: (put) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (put) request: #{uri.path}" if @debug + puts "DEBUG: (put) data: #{@data}" if @debug + puts "DEBUG: (put) return code: #{res.code}" if @debug + puts "DEBUG: (put) return body: #{res.body}" if @debug + + [res.body, res.code.to_i ] +end + +def delete_json(path) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Delete,uri) + + puts "DEBUG: (d) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (d) request: #{uri.path}" if @debug + puts "DEBUG: (d) return code: #{res.code}" if @debug + puts "DEBUG: (d) return body: #{res.body}" if @debug + + [res.body, res.code.to_i ] +end + + +def list + struct = get_json("/") + + if struct[1] != 200 + [ "Failed to talk to service list: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No current configurations", 0 ] + else + out = "" + struct[0].each do |name| + out = out + "\n" if out != "" + out = out + "#{name}" + end + [ out, 0 ] + end +end + +def api_help + struct=get_json("/help") + if struct[1] != 200 + [ "Failed to talk to service list: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No help", 0 ] + else + [ jj(struct[0]), 0 ] + end +end + +def show(name) + usage -1 if name.nil? or name == "" + + struct = get_json("/#{name}") + + if struct[1] == 200 + [ "#{JSON.pretty_generate(struct[0])}", 0 ] + elsif struct[1] == 404 + [ "No current configuration for #{name}", 1 ] + else + [ "Failed to talk to service show: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def delete(name) + usage -1 if name.nil? or name == "" + + struct = delete_json("/#{name}") + + if struct[1] == 200 + [ "Deleted #{name}", 0 ] + elsif struct[1] == 404 + [ "Delete failed for #{name}: Not Found", 1 ] + else + [ "Failed to talk to service delete: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def proposal_list + struct = get_json("/proposals/") + + if struct[1] != 200 + [ "Failed to talk to service proposal list: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No current proposals", 0 ] + else + out = "" + struct[0].each do |name| + out = out + "\n" if out != "" + out = out + "#{name}" + end + [ out, 0 ] + end +end + +def proposal_show(name) + usage -1 if name.nil? or name == "" + + struct = get_json("/proposals/#{name}") + + if struct[1] == 200 + [ "#{JSON.pretty_generate(struct[0])}", 0 ] + elsif struct[1] == 404 + [ "No current proposal for #{name}", 1 ] + else + [ "Failed to talk to service proposal show: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def proposal_create(name) + usage -1 if name.nil? or name == "" + + @data = "{\"id\":\"#{name}\"}" if @data.nil? or @data == "" + + struct = put_json("/proposals", @data) + + if struct[1] == 200 + [ "Created #{name}", 0 ] + else + [ "Failed to talk to service proposal create: #{struct[1]}: #{struct[0]}", 1] + end +end + +def proposal_edit(name) + usage -1 if name.nil? or name == "" + + struct = post_json("/proposals/#{name}", @data) + + if struct[1] == 200 + [ "Edited #{name}", 0 ] + elsif struct[1] == 404 + [ "Failed to edit: #{name} : Not Found", 1 ] + elsif struct[1] == 400 + [ "Failed to edit: #{name} : Errors in data\n#{struct[0]}", 1 ] + else + [ "Failed to talk to service proposal edit: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def proposal_delete(name) + usage -1 if name.nil? or name == "" + + struct = delete_json("/proposals/#{name}") + + if struct[1] == 200 + [ "Deleted #{name}", 0 ] + elsif struct[1] == 404 + [ "Delete failed for #{name}: Not Found", 1 ] + else + [ "Failed to talk to service delete: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def proposal_commit(name) + usage -1 if name.nil? or name == "" + + struct = post_json("/proposals/commit/#{name}", @data) + + if struct[1] == 200 + [ "Committed #{name}", 0 ] + elsif struct[1] == 202 + [ "Queued #{name} because #{struct[0]}", 0 ] + else + [ "Failed to talk to service proposal commit: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def proposal_dequeue(name) + usage -1 if name.nil? or name == "" + + struct = post_json("/proposals/dequeue/#{name}", @data) + + if struct[1] == 200 + [ "Dequeued #{name}", 0 ] + else + [ "Failed to talk to service proposal dequeue: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def elements + struct = get_json("/elements") + + if struct[1] != 200 + [ "Failed to talk to service elements: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No current elements", 1 ] + else + out = "" + struct[0].each do |name| + out = out + "\n" if out != "" + out = out + "#{name}" + end + [ out, 0 ] + end +end + +def element_node(element) + usage -1 if element.nil? or element == "" + + struct = get_json("/elements/#{element}") + + if struct[1] != 200 + [ "Failed to talk to service element_node: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No nodes for #{element}", 1 ] + else + out = "" + struct[0].each do |name| + out = out + "\n" if out != "" + out = out + "#{name}" + end + [ out, 0 ] + end +end + +def transition(name, state) + usage -1 if name.nil? or name == "" + + data = { + "name" => name, + "state" => state + } + struct = post_json("/transition/default", data.to_json) + + if struct[1] == 200 + [ "Transitioned #{name}", 0 ] + else + [ "Failed to talk to service transition: #{struct[1]}: #{struct[0]}", 1 ] + end +end + + + +### Start MAIN ### + +def opt_parse() + sub_options = @options.map { |x| x[0] } + lsub_options = @options.map { |x| [ x[0][0], x[2] ] } + opts = GetoptLong.new(*sub_options) + + opts.each do |opt, arg| + case opt + when '--help' + usage 0 + when '--debug' + @debug = true + when '--hostname' + @hostname = arg + when '--username' + @username = arg + when '--password' + @password = arg + when '--port' + @port = arg.to_i + when '--data' + @data = arg + when '--timeout' + @timeout = arg + when '--file' + @data = File.read(arg) + else + found = false + lsub_options.each do |x| + next if x[0] != opt + eval x[1] + found = true + end + usage -1 unless found + end + end + + if ARGV.length == 0 and !@allow_zero_args + usage -1 + end + + STDERR.puts "CROWBAR_KEY not set, will not be able to authenticate!" if @username.nil? or @password.nil? + STDERR.puts "Please set CROWBAR_KEY or use -U and -P" if @username.nil? or @password.nil? +end + +def run_sub_command(cmds, subcmd) + cmd = cmds[subcmd] + usage -2 if cmd.nil? + eval cmd[0] +end + +def run_command() + run_sub_command(@commands, ARGV.shift) +end + +def main() + opt_parse + res = run_command + puts res[0] + exit res[1] +end diff --git a/crowbar/change-image/dell/barclamps/crowbar/Makefile b/crowbar/change-image/dell/barclamps/crowbar/Makefile new file mode 100644 index 00000000000..6584fe2a4da --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-crowbar" + +distclean: + @echo "Dist-Cleaning barclamp-crowbar" + +all: clean build install + +build: + @echo "Building barclamp-crowbar" + +install: + @echo "Installing barclamp-crowbar" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-crowbar + cp -r chef ${DESTDIR}/usr/share/barclamp-crowbar + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/crowbar/app/controllers/crowbar_controller.rb b/crowbar/change-image/dell/barclamps/crowbar/app/controllers/crowbar_controller.rb new file mode 100644 index 00000000000..13ebcf3d7ec --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/app/controllers/crowbar_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class CrowbarController < BarclampController + def initialize + @service_object = CrowbarService.new(logger) + end +end + diff --git a/crowbar/change-image/dell/barclamps/crowbar/app/models/crowbar_service.rb b/crowbar/change-image/dell/barclamps/crowbar/app/models/crowbar_service.rb new file mode 100644 index 00000000000..7ee17a5e540 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/app/models/crowbar_service.rb @@ -0,0 +1,203 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class CrowbarService < ServiceObject + + def initialize(thelogger) + @bc_name = "crowbar" + @logger = thelogger + end + + # + # Below are the parts to handle transition requests. + # + # This routine handles name-based state transitions. The system will then inform barclamps. + # It will create a node and assign it an admin address. + # + def transition(inst, name, state) + save_it = false + + @logger.info("Crowbar transition enter: #{name} to #{state}") + + node = NodeObject.find_node_by_name name + if node.nil? and (state == "discovering" or state == "testing") + @logger.debug("Crowbar transition: creating new node for #{name} to #{state}") + node = Chef::Node.new + node["crowbar"] = {} + + node[:fqdn] = name + node.name name + + node = NodeObject.new node + node.save + end + if node.nil? + @logger.error("Crowbar transition leaving: node not found nor created - #{name} to #{state}") + return [200, {}] + end + + node["crowbar"] = {} if node["crowbar"].nil? + node["crowbar"]["network"] = {} if node["crowbar"]["network"].nil? + node["crowbar"]["decoration"] = { :name=> name } + + pop_it = false + if node[:state] != state + @logger.debug("Crowbar transition: state has changed so we need to do stuff for #{name} to #{state}") + + node["crowbar"]["state_debug"] = {} if node["crowbar"]["state_debug"].nil? + if node["crowbar"]["state_debug"][state].nil? + node["crowbar"]["state_debug"][state] = 1 + else + node["crowbar"]["state_debug"][state] = node["crowbar"]["state_debug"][state] + 1 + end + + node[:state] = state + save_it = true + pop_it = true + end + + node.save if save_it + + if pop_it + crole = RoleObject.find_role_by_name("crowbar-config-#{inst}") + ro = crole.default_attributes["crowbar"]["run_order"] + + # + # If we are discovering the node and it is an admin, make sure that we add the crowbar config + # + if state == "discovering" and node.admin? + db = ProposalObject.find_proposal("crowbar", inst) + add_role_to_instance_and_node("crowbar", inst, name, db, crole, "crowbar") + end + + roles = RoleObject.find_roles_by_search "transitions:true AND (transition_list:all OR transition_list:#{ChefObject.chef_escape(state)})" + # Make sure the deployer objects run first. + roles.sort! do |x,y| + xname = x.name.gsub(/-config-.*$/, "") + yname = y.name.gsub(/-config-.*$/, "") + + xs = 1000 + xs = ro[xname] unless ro[xname].nil? + ys = 1000 + ys = ro[yname] unless ro[yname].nil? + xs <=> ys + end + + roles.each do |role| + role.override_attributes.each do |bc, data| + jsondata = { + "name" => name, + "state" => state + } + rname = role.name.gsub("#{bc}-config-","") + begin + @logger.info("Crowbar transition: calling #{bc}:#{rname} for #{name} for #{state}") + service = eval("#{bc.capitalize}Service.new @logger") + answer = service.transition(rname, name, state) + if answer[0] != 200 + @logger.error("Crowbar transition: finished #{bc}:#{rname} for #{name} for #{state}: FAILED #{answer[1]}") + else + @logger.debug("Crowbar transition: finished #{bc}:#{rname} for #{name} for #{state}") + unless answer[1]["name"].nil? + name = answer[1]["name"] + end + end + rescue Exception => e + @logger.fatal("json/transition for #{bc}:#{rname} failed: #{e.message}") + @logger.fatal("#{e.backtrace}") + end + end + end + + # We have a node that has become ready, test to see if there are queued proposals to commit + process_queue if state == "ready" + end + + @logger.debug("Crowbar transition leaving: #{name} to #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + + def create_proposal + @logger.debug("Crowbar create_proposal enter") + base = super + @logger.debug("Crowbar create_proposal exit") + base + end + + def apply_role (role, inst) + @logger.debug("Crowbar apply_role: enter") + answer = super + @logger.debug("Crowbar apply_role: super apply_role finished") + + node = role.default_attributes + @logger.debug("Crowbar apply_role: create initial instances") + unless node["crowbar"].nil? or node["crowbar"]["instances"].nil? + node["crowbar"]["instances"].each do |k,plist| + plist.each do |v| + id = "default" + data = "{\"id\":\"#{id}\"}" + if v != "default" + file = File.open(v, "r") + data = file.readlines.to_s + file.close + + struct = JSON.parse(data) + id = struct["id"].gsub("bc-#{k}-", "") + end + + @logger.debug("Crowbar apply_role: creating #{k}.#{id}") + + # Create a service to talk to. + service = eval("#{k.capitalize}Service.new @logger") + + @logger.debug("Crowbar apply_role: Calling get to see if it already exists: #{k}.#{id}") + answer = service.proposals + if answer[0] != 200 + @logger.error("Failed to list #{k}: #{answer[0]} : #{answer[1]}") + else + unless answer[1].include?(id) + @logger.debug("Crowbar apply_role: didn't already exist, creating proposal for #{k}.#{id}") + answer = service.proposal_create JSON.parse(data) + if answer[0] != 200 + @logger.error("Failed to create #{k}.#{id}: #{answer[0]} : #{answer[1]}") + end + end + + @logger.debug("Crowbar apply_role: check to see if it is already active: #{k}.#{id}") + answer = service.list_active + if answer[0] != 200 + @logger.error("Failed to list active #{k}: #{answer[0]} : #{answer[1]}") + else + unless answer[1].include?(id) + @logger.debug("Crowbar apply_role: #{k}.#{id} wasn't active: Activating") + answer = service.proposal_commit id + if answer[0] != 200 + @logger.error("Failed to commit #{k}.#{id}: #{answer[0]} : #{answer[1]}") + end + end + end + end + + @logger.fatal("Crowbar apply_role: Done with creating: #{k}.#{id}") + end + end + end + + @logger.debug("Crowbar apply_role: leaving: #{answer}") + answer + end + +end + diff --git a/crowbar/change-image/dell/barclamps/crowbar/barclamp-crowbar.spec b/crowbar/change-image/dell/barclamps/crowbar/barclamp-crowbar.spec new file mode 100644 index 00000000000..52fdbd76929 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/barclamp-crowbar.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-crowbar +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: Core functionaly within Crowbar +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages crowbar deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-crowbar/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-crowbar/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-crowbar/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/crowbar/build_deb.sh b/crowbar/change-image/dell/barclamps/crowbar/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/crowbar/build_rpm.sh b/crowbar/change-image/dell/barclamps/crowbar/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/README.rdoc b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/README.rdoc new file mode 100644 index 00000000000..bddd59c4be5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/README.rdoc @@ -0,0 +1,51 @@ += DESCRIPTION: + + += REQUIREMENTS: + +== Platform: + + +== Cookbooks: + + +* ruby +* apache2 +* passenger + += ATTRIBUTES: + +* rails[:version] - Install the specified version. Default false (installs latest). +* rails[:environment] - Set Rails environment. Default production. + += USAGE: + +The recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define. + + include_recipe "apache2" + include_recipe "passenger" + include_recipe "rails" + + web_app "some_rails_app" do + docroot "/srv/some_rails_app/public" + template "some_rails_app.conf.erb" + end + +We provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/crowbar b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/crowbar new file mode 100755 index 00000000000..8a073a4238f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/crowbar @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Mark the admin node as ready on reboot. +# + +IP="127.0.0.1" + +exec 2>>/var/log/crowbar-join.errlog + +export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): ' +set -x + +if [[ -f /etc/crowbar.install.key ]]; then + export CROWBAR_KEY="$(cat /etc/crowbar.install.key)" +fi + +# Run a command and log its output. +log_to() { + # $1 = install log to log to + # $@ = rest of args + local __log="/var/log/install-$1" + local __timestamp="$(date '+%F %T %z')" + shift + printf "\n%s\n" "$__timestamp: Running $*" | \ + tee -a "$__log.err" >> "$__log.log" + local _ret=0 + if "$@" 2>> "$__log.err" >>"$__log.log"; then + _ret=0 + else + _ret="$?" + echo "$__timestamp: $* failed." + echo "See $__log.log and $__log.err for more information." + fi + printf "\n$s\n--------\n" "$(date '+%F %T %z'): Done $*" | \ + tee -a "$__log.err" >> "$__log.log" + return $_ret +} + +post_state() { + local curlargs=(-o "/var/log/$1-$2.json" --connect-timeout 60 -s \ + -L -X POST --data-binary "{ \"name\": \"$1\", \"state\": \"$2\" }" \ + -H "Accept: application/json" -H "Content-Type: application/json" \ + --max-time 240) + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) + curl "${curlargs[@]}" "http://$IP:3000/crowbar/crowbar/1.0/transition/default" +} + +# Mark us as readying, and get our cert. +post_state $HOSTNAME "readying" +post_state $HOSTNAME "ready" + +echo "Done" diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/crowbar.sh b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/crowbar.sh new file mode 100644 index 00000000000..5336ffd4f43 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/crowbar.sh @@ -0,0 +1,15 @@ +# +# Default path variables. +# + +# Make sure /opt/dell/bin is in the root path +if ! echo ${PATH} | /bin/grep -q /opt/dell/bin ; then + if [ `/usr/bin/id -u` = 0 ] ; then + PATH=${PATH}:/opt/dell/bin + fi +fi + +if [ -f /etc/crowbar.install.key ] ; then + export CROWBAR_KEY=`cat /etc/crowbar.install.key` +fi + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/knife.rb b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/knife.rb new file mode 100644 index 00000000000..7418889f085 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/files/default/knife.rb @@ -0,0 +1,2 @@ +node_name "chef-webui" +client_key "/etc/chef/webui.pem" diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/metadata.json b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/metadata.json new file mode 100644 index 00000000000..5edb1128e35 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/metadata.json @@ -0,0 +1,58 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + "apache2": [ + + ], + "ruby": [ + + ], + "passenger_apache2": [ + + ], + "rails": [ + + ] + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "ubuntu": [ + + ], + "redhat": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "crowbar": "Installs crowbar", + "crowbar::local_apt_repo": "Makes the local node a repos to itself" + }, + "maintainer_email": "crowbar@dell.com", + "name": "crowbar", + "conflicting": { + }, + "description": "Installs crowbar", + "long_description": "" + } diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/providers/user.rb b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/providers/user.rb new file mode 100644 index 00000000000..b2567ba2d3b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/providers/user.rb @@ -0,0 +1,19 @@ + +action :add do + filename = "/opt/dell/openstack_manager/htdigest" + digest = Digest::MD5.hexdigest("#{new_resource.name}:#{new_resource.description}:#{new_resource.password}") + utils_line "#{new_resource.name}:#{new_resource.description}:#{digest}" do + action :add + file filename + end +end + +action :remove do + filename = "/opt/dell/openstack_manager/htdigest" + digest = Digest::MD5.hexdigest("#{new_resource.name}:#{new_resource.description}:#{new_resource.password}") + utils_line "#{new_resource.name}:#{new_resource.description}:#{digest}" do + action :remove + file filename + end +end + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/recipes/default.rb b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/recipes/default.rb new file mode 100644 index 00000000000..9392a329757 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/recipes/default.rb @@ -0,0 +1,135 @@ +# +# Cookbook Name:: crowbar +# Recipe:: default +# +# Copyright 2011, Opscode, Inc. and Dell, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "apache2" +include_recipe "apache2::mod_auth_digest" +include_recipe "passenger_apache2" +include_recipe "rails" + +package "ipmitool" + +group "crowbar" + +user "crowbar" do + comment "Crowbar" + gid "crowbar" + home "/home/random" + shell "/bin/false" +end + +directory "/root/.chef" do + owner "root" + group "root" + mode "0700" + action :create +end + +cookbook_file "/etc/profile.d/crowbar.sh" do + owner "root" + group "root" + mode "0755" + action :create + source "crowbar.sh" +end + +cookbook_file "/root/.chef/knife.rb" do + owner "root" + group "root" + mode "0600" + action :create + source "knife.rb" +end + +directory "/home/openstack/.chef" do + owner "openstack" + group "openstack" + mode "0700" + action :create +end + +cookbook_file "/home/openstack/.chef/knife.rb" do + owner "openstack" + group "openstack" + mode "0600" + action :create + source "knife.rb" +end + +bash "Add crowbar chef client" do + environment ({'EDITOR' => '/bin/true'}) + code "knife client create crowbar -a --file /opt/dell/openstack_manager/config/client.pem -u chef-validator -k /etc/chef/validation.pem" + not_if "knife client list -u crowbar -k /opt/dell/openstack_manager/config/client.pem" +end + +file "/opt/dell/openstack_manager/log/production.log" do + owner "crowbar" + group "crowbar" + mode "0666" + action :create +end + +file "/opt/dell/openstack_manager/tmp/queue.lock" do + owner "crowbar" + group "crowbar" + mode "0644" + action :create +end +file "/opt/dell/openstack_manager/tmp/ip.lock" do + owner "crowbar" + group "crowbar" + mode "0644" + action :create +end + +# Add crowbar users - system added machine-install in install-chef.sh +node["crowbar"]["users"].each do |user,hash| + crowbar_user user do + action :add + description hash["description"] + password hash["password"] + end +end unless node["crowbar"].nil? or node["crowbar"]["users"].nil? + +bash "set permissions" do + code "chown -R crowbar:crowbar /opt/dell/openstack_manager" + not_if "ls -al /opt/dell/openstack_manager/README | grep -q crowbar" +end + +web_app "crowbar_app" do + server_name node[:fqdn] + docroot "/opt/dell/openstack_manager/public" + template "crowbar_app.conf.erb" +end + +cookbook_file "/etc/init.d/crowbar" do + owner "root" + group "root" + mode "0755" + action :create + source "crowbar" +end + +["3", "5", "2"].each do |i| + link "/etc/rc#{i}.d/S99xcrowbar" do + action :create + to "/etc/init.d/crowbar" + not_if "test -L /etc/rc#{i}.d/S99xcrowbar" + end +end + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/recipes/local_apt_repo.rb b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/recipes/local_apt_repo.rb new file mode 100644 index 00000000000..750c0984f41 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/recipes/local_apt_repo.rb @@ -0,0 +1,14 @@ + +apt_repository "local maverick main" do + uri "file:///tftpboot/ubuntu_dvd" + distribution "maverick" + components [ "main", "restricted" ] + action :add +end + +apt_repository "local extra repos" do + uri "file:///tftpboot/ubuntu_dvd/extra/" + distribution "/" + action :add +end + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/resources/user.rb b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/resources/user.rb new file mode 100644 index 00000000000..758ea1655c3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/resources/user.rb @@ -0,0 +1,7 @@ + +actions :add, :remove + +attribute :name, :kind_of => String, :name_attribute => true +attribute :description, :kind_of => String +attribute :password, :kind_of => String + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/templates/default/crowbar_app.conf.erb b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/templates/default/crowbar_app.conf.erb new file mode 100644 index 00000000000..bdcb9ba5f56 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/cookbooks/crowbar/templates/default/crowbar_app.conf.erb @@ -0,0 +1,40 @@ +Listen 3000 + + ServerName <%= @params[:server_name] %> + DocumentRoot <%= @params[:docroot] %> + + + AuthType Digest + AuthName "Crowbar" + AuthDigestDomain http://192.168.124.10:3000/ + AuthDigestProvider file + AuthUserFile /opt/dell/openstack_manager/htdigest + Require valid-user + + + RailsBaseURI / + RailsMaxPoolSize <%= node[:rails][:max_pool_size] %> + RailsPoolIdleTime 3600 + RailsEnv '<%= node[:rails][:environment] %>' + + LogLevel info + ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>_error.log + CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>_access.log combined + + ErrorDocument 404 /404.html + ErrorDocument 500 /500.html + + RewriteEngine On + + # Handle maintenance mode + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^/(.*)$ /system/maintenance.html [L] + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/data_bags/crowbar/bc-template-crowbar.json b/crowbar/change-image/dell/barclamps/crowbar/chef/data_bags/crowbar/bc-template-crowbar.json new file mode 100644 index 00000000000..24955df771d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/data_bags/crowbar/bc-template-crowbar.json @@ -0,0 +1,68 @@ +{ + "id": "bc-template-crowbar", + "description": "Self-referential barclamp enabling other barclamps", + "attributes": { + "crowbar": { + "barclamps": [ + "crowbar", + "deployer", + "ipmi", + "network", + "provisioner", + "ntp", + "dns", + "nagios", + "ganglia", + "logging" + ], + "run_order": { + "deployer": 10, + "ipmi": 17, + "network": 20, + "dns": 30, + "logging": 40, + "ntp": 50, + "nagios": 70, + "ganglia": 80, + "provisioner": 1060 + }, + "instances": { + "deployer": [ "default" ], + "ipmi": [ "default" ], + "provisioner": [ "default" ], + "network": [ "default" ], + "ntp": [ "default" ], + "dns": [ "default" ], + "nagios": [ "default" ], + "ganglia": [ "default" ], + "logging": [ "default" ] + }, + "users": { + "crowbar": { "password": "crowbar", "description": "Crowbar" } + } + }, + "rails": { + "max_pool_size": 256, + "environment": "production" + }, + "passenger": { + "max_pool_size": 256 + } + }, + "deployment": { + "crowbar": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "crowbar" ] + ], + "config": { + "environment": "crowbar-config-test", + "mode": "full", + "transitions": false, + "transition_list": [ ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/data_bags/crowbar/bc-template-crowbar.schema b/crowbar/change-image/dell/barclamps/crowbar/chef/data_bags/crowbar/bc-template-crowbar.schema new file mode 100644 index 00000000000..f185b273746 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/data_bags/crowbar/bc-template-crowbar.schema @@ -0,0 +1,88 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-crowbar-|^bc-template-crowbar$/" }, + "description": { "type": "str", "required": true }, + "attributes": { "type": "map", "required": true, "mapping": { + "crowbar": { "type": "map", "required": true, "mapping": { + "barclamps": { "type": "seq", "required": true, "sequence": [ { "type": "str" } ] }, + "run_order": { "type": "map", "required": true, "mapping": { + = : { "type": "int", "required": true } + } + }, + "instances": { "type": "map", "required": true, "mapping": { + = : { "type": "seq", "required": true, "sequence": [ { "type": "str" } ] } + } + }, + "users": { "type": "map", "required": true, "mapping": { + = : { "type": "map", "required": true, "mapping": { + "password": { "type": "str", "required": true }, + "description": { "type": "str", "required": true } + } + } + } + } + } + }, + "rails": { "type": "map", "required": true, "mapping": { + "environment": { "type": "str", "required": true }, + "max_pool_size": { "type": "int", "required": true } + } + }, + "passenger": { "type": "map", "required": true, "mapping": { + "max_pool_size": { "type": "int", "required": true } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "crowbar": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/crowbar/chef/roles/crowbar.rb b/crowbar/change-image/dell/barclamps/crowbar/chef/roles/crowbar.rb new file mode 100644 index 00000000000..70536941b29 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/chef/roles/crowbar.rb @@ -0,0 +1,24 @@ + +name "crowbar" +description "Crowbar role - Setups the rails app" +run_list( + "recipe[crowbar::local_apt_repo]", + "recipe[utils]", + "recipe[apt]", + "recipe[sudo]", + "recipe[crowbar]" +) +default_attributes( + :crowbar => { :admin_node => true }, + :rails => { :max_pool_size => 256, :environment => "production" }, + :passenger => { :max_pool_size => 256 }, + :authorization => { + :sudo => { + :groups => [ "admin" ], + :users => [ "crowbar" ], + :passwordless => true + } + } +) +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar new file mode 100755 index 00000000000..43149b01a6c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +loc=File.expand_path(File.dirname(__FILE__)) +def usage (rc) + puts "Usage: crowbar " + puts " Areas: #{@areas.join(" ")}" + exit rc +end + +@areas = Dir.glob("./crowbar_*").map! { |x| x.gsub("./crowbar_", "") } +@areas << Dir.glob(File.join(loc,"crowbar_*")).map! { |x| x.gsub(File.join(loc,"crowbar_"), "") } +@areas = @areas.flatten + +if ARGV.length == 0 + usage -1 +end + +area = ARGV.shift + +usage -1 unless @areas.include?(area) + +exec("./crowbar_#{area}", *ARGV) if File.exists?("./crowbar_#{area}") +exec(File.join(loc,"crowbar_#{area}"), *ARGV) if File.exists?(File.join(loc,"crowbar_#{area}")) diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_crowbar b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_crowbar new file mode 100755 index 00000000000..7fff66449e6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_crowbar @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "crowbar" + +main diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_machines b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_machines new file mode 100755 index 00000000000..3b2c128633d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_machines @@ -0,0 +1,331 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'net/http' +require 'net/http/digest_auth' +require 'uri' +require 'json' +require 'getoptlong' + +@debug = false +@hostname = ENV["CROWBAR_IP"] +# DO NOT CHANGE THE FORMAT OF THE NEXT 2 LINES. +# gather_cli relies on the exact spacing of them to +# rewrite the addreses when it is serving the CLI. +@hostname = "127.0.0.1" unless @hostname +@port = 3000 +@headers = { + "Accept" => "application/json", + "Content-Type" => "application/json" +} +@data = "" +@allow_zero_args = false +@timeout = 500 +@barclamp="machines" +@key = ENV["CROWBAR_KEY"] +if @key + @username=@key.split(':',2)[0] + @password=@key.split(':',2)[1] +end + +# +# Parsing options can be added by adding to this list before calling opt_parse +# +@options = [ + [ [ '--help', '-h', GetoptLong::NO_ARGUMENT ], "--help or -h - help" ], + [ [ '--username', '-U', GetoptLong::REQUIRED_ARGUMENT ], "--username or -U - specifies the username to use" ], + [ [ '--password', '-P', GetoptLong::REQUIRED_ARGUMENT ], "--password or -P - specifies the password to use" ], + [ [ '--hostname', '-n', GetoptLong::REQUIRED_ARGUMENT ], "--hostname or -n - specifies the destination server" ], + [ [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], "--port or -p - specifies the destination server port" ], + [ [ '--debug', '-d', GetoptLong::NO_ARGUMENT ], "--debug or -d - turns on debugging information" ], + [ [ '--data', GetoptLong::REQUIRED_ARGUMENT ], "--data - used by create or edit as data (must be in json format)" ], + [ [ '--file', GetoptLong::REQUIRED_ARGUMENT ], "--file - used by create or edit as data when read from a file (must be in json format)" ], + [ [ '--timeout', GetoptLong::REQUIRED_ARGUMENT ], "--timeout - timeout in seconds for read http reads" ] +] + +@commands = { + "help" => [ "help", "help - this page" ], + "api_help" => [ "api_help", "crowbar API help - help for this barclamp." ], + "list" => [ "list", "list - show a list of current configs" ], + "show" => [ "show ARGV.shift, ARGV.shift", "show [arg] - show a specific config" ], + "create" => [ "create ARGV.shift", "create - create a specific config" ], + "edit" => [ "edit ARGV.shift", "edit - edit a new config" ], + "delete" => [ "delete ARGV.shift", "delete - delete a node" ], + "reboot" => [ "action \"reboot\", ARGV.shift", "reboot - reboot a node" ], + "shutdown" => [ "action \"shutdown\", ARGV.shift", "shutdown - shutdown a node" ], + "poweron" => [ "action \"poweron\", ARGV.shift", "poweron - poweron a node" ], + "identify" => [ "action \"identify\", ARGV.shift", "identify - identify a node" ], + "allocate" => [ "action \"allocate\", ARGV.shift", "allocate - allocate a node" ], + "reset" => [ "action \"reset\", ARGV.shift", "reset - reset a node" ], + "reinstall" => [ "action \"reinstall\", ARGV.shift", "reinstall - reinstall a node" ] +} + + +def print_commands(cmds, spacer = " ") + cmds.each do |key, command| + puts "#{spacer}#{command[1]}" + print_commands(command[2], " #{spacer}") if command[0] =~ /run_sub_command\(/ + end +end + +def usage (rc) + puts "Usage: crowbar #{@barclamp} [options] " + @options.each do |options| + puts " #{options[1]}" + end + print_commands(@commands) + exit rc +end + +def help + usage 0 +end + +def authenticate(req,uri,data=nil) + uri.user=@username + uri.password=@password + res=nil + Net::HTTP.start(uri.host, uri.port) {|http| + r = req.new(uri.request_uri,@headers) + r.body = data if data + res = http.request r + if res['www-authenticate'] + digest_auth=Net::HTTP::DigestAuth.new + auth=Net::HTTP::DigestAuth.new.auth_header(uri, + res['www-authenticate'], + req::METHOD) + r.add_field 'Authorization', auth + res = http.request r + end + } + res +end + +def get_json(path) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Get,uri) + + puts "DEBUG: (g) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (g) request: #{uri.path}" if @debug + puts "DEBUG: (g) return code: #{res.code}" if @debug + puts "DEBUG: (g) return body: #{res.body}" if @debug + + return [res.body, res.code.to_i ] if res.code.to_i != 200 + + struct = JSON.parse(res.body) + + puts "DEBUG: (g) JSON parse structure = #{struct.inspect}" if @debug + + return [struct, 200] +end + +def post_json(path, data) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Post,uri,data) + + puts "DEBUG: (post) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (post) request: #{uri.path}" if @debug + puts "DEBUG: (post) data: #{@data}" if @debug + puts "DEBUG: (post) return code: #{res.code}" if @debug + puts "DEBUG: (post) return body: #{res.body}" if @debug + + [res.body, res.code.to_i ] +end + +def put_json(path, data) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Put,uri,data) + + puts "DEBUG: (put) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (put) request: #{uri.path}" if @debug + puts "DEBUG: (put) data: #{@data}" if @debug + puts "DEBUG: (put) return code: #{res.code}" if @debug + puts "DEBUG: (put) return body: #{res.body}" if @debug + + [res.body, res.code.to_i ] +end + +def delete_json(path) + uri = URI.parse("http://#{@hostname}:#{@port}/crowbar/#{@barclamp}/1.0#{path}") + res = authenticate(Net::HTTP::Delete,uri) + + puts "DEBUG: (d) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (d) request: #{uri.path}" if @debug + puts "DEBUG: (d) return code: #{res.code}" if @debug + puts "DEBUG: (d) return body: #{res.body}" if @debug + + [res.body, res.code.to_i ] +end + +def list + struct = get_json("/") + + if struct[1] != 200 + [ "Failed to talk to service list: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No current configurations", 0 ] + else + out = "" + struct[0].each do |name| + out = out + "\n" if out != "" + out = out + "#{name}" + end + [ out, 0 ] + end +end + +def api_help + struct=get_json("/help") + if struct[1] != 200 + [ "Failed to talk to service list: #{struct[1]}: #{struct[0]}", 1 ] + elsif struct[0].nil? or struct[0].empty? + [ "No help", 0 ] + else + [ jj(struct[0]), 0 ] + end +end + +def show(name, field = nil) + usage -1 if name.nil? or name == "" + + struct = get_json("/0?name=#{name}") + + if struct[1] == 200 + if field.nil? + [ "#{JSON.pretty_generate(struct[0])}", 0 ] + else + data = struct[0] + fields = field.split(".") + fields.each { |x| data = data[x] } + + [ "#{JSON.pretty_generate(data)}", 0 ] + end + elsif struct[1] == 404 + [ "No current configuration for #{name}", 1 ] + else + [ "Failed to talk to service show: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def create(name) + usage -1 if name.nil? or name == "" + + @data = "{\"name\":\"#{name}\"}" if @data.nil? or @data == "" + + struct = put_json("/", @data) + + if struct[1] == 200 + [ "Created #{name}", 0 ] + else + [ "Failed to talk to service create: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def edit(name) + usage -1 if name.nil? or name == "" + + struct = post_json("/#{name}", @data) + + if struct[1] == 200 + [ "Edited #{name}", 0 ] + elsif struct[1] == 404 + [ "Failed to edit: #{name} : Not Found", 1 ] + elsif struct[1] == 400 + [ "Failed to edit: #{name} : Errors in data\n#{struct[0]}", 1 ] + else + [ "Failed to talk to service edit: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def delete(name) + usage -1 if name.nil? or name == "" + + struct = delete_json("?name=#{name}") + + if struct[1] == 200 + [ "Deleted #{name}", 0 ] + elsif struct[1] == 404 + [ "Delete failed for #{name}: Not Found", 1 ] + else + [ "Failed to talk to service delete: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def action(aaa, name) + usage -1 if name.nil? or name == "" + + @data = "{\"name\":\"#{name}\"}" if @data.nil? or @data == "" + + struct = post_json("/#{aaa}/0", @data) + + if struct[1] == 200 + [ "#{aaa} #{name}", 0 ] + else + [ "Failed to talk to service #{aaa}: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +### Start MAIN ### + +def opt_parse() + sub_options = @options.map { |x| x[0] } + lsub_options = @options.map { |x| [ x[0][0], x[2] ] } + opts = GetoptLong.new(*sub_options) + + opts.each do |opt, arg| + case opt + when '--help' + usage 0 + when '--debug' + @debug = true + when '--hostname' + @hostname = arg + when '--username' + @username = arg + when '--password' + @password = arg + when '--port' + @port = arg.to_i + when '--data' + @data = arg + when '--timeout' + @timeout = arg + when '--file' + data = File.read(arg, "r") + else + found = false + lsub_options.each do |x| + next if x[0] != opt + eval x[1] + found = true + end + usage -1 unless found + end + end + + STDERR.puts "CROWBAR_KEY not set, will not be able to authenticate!" if @username.nil? or @password.nil? + STDERR.puts "Please set CROWBAR_KEY or use -U and -P" if @username.nil? or @password.nil? + if ARGV.length == 0 and !@allow_zero_args + usage -1 + end +end + +def run_sub_command(cmds, subcmd) + cmd = cmds[subcmd] + usage -2 if cmd.nil? + eval cmd[0] +end + +def run_command() + run_sub_command(@commands, ARGV.shift) +end + +def main() + opt_parse + res = run_command + puts res[0] + exit res[1] +end + +main + diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_node_state b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_node_state new file mode 100755 index 00000000000..7e5bc71cfac --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_node_state @@ -0,0 +1,184 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'net/http' +require 'net/http/digest_auth' +require 'uri' +require 'json' +require 'getoptlong' + +@debug = false +@hostname = ENV["CROWBAR_IP"] +# DO NOT CHANGE THE FORMAT OF THE NEXT 2 LINES. +# gather_cli relies on the exact spacing of them to +# rewrite the addreses when it is serving the CLI. +@hostname = "127.0.0.1" unless @hostname +@port = 3000 +@headers = { + "Accept" => "application/json", + "Content-Type" => "application/json" +} +@data = "" +@allow_zero_args = false +@timeout = 500 +@barclamp="machines" +@key = ENV["CROWBAR_KEY"] +if @key + @username=@key.split(':',2)[0] + @password=@key.split(':',2)[1] +end + +# +# Parsing options can be added by adding to this list before calling opt_parse +# +@options = [ + [ [ '--help', '-h', GetoptLong::NO_ARGUMENT ], "--help or -h - help" ], + [ [ '--username', '-U', GetoptLong::REQUIRED_ARGUMENT ], "--username or -U - specifies the username to use" ], + [ [ '--password', '-P', GetoptLong::REQUIRED_ARGUMENT ], "--password or -P - specifies the password to use" ], + [ [ '--hostname', '-n', GetoptLong::REQUIRED_ARGUMENT ], "--hostname or -n - specifies the destination server" ], + [ [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], "--port or -p - specifies the destination server port" ], + [ [ '--debug', '-d', GetoptLong::NO_ARGUMENT ], "--debug or -d - turns on debugging information" ], + [ [ '--timeout', GetoptLong::REQUIRED_ARGUMENT ], "--timeout - timeout in seconds for read http reads" ] +] + +@commands = { + "status" => [ "status", "status - show status of nodes" ], +} + + +def print_commands(cmds, spacer = " ") + cmds.each do |key, command| + puts "#{spacer}#{command[1]}" + print_commands(command[2], " #{spacer}") if command[0] =~ /run_sub_command\(/ + end +end + +def usage (rc) + puts "Usage: crowbar node_state [options] " + @options.each do |options| + puts " #{options[1]}" + end + print_commands(@commands) + exit rc +end + +def help + usage 0 +end + +def authenticate(req,uri,data=nil) + uri.user=@username + uri.password=@password + res=nil + Net::HTTP.start(uri.host, uri.port) {|http| + r = req.new(uri.request_uri,@headers) + r.body = data if data + res = http.request r + if res['www-authenticate'] + digest_auth=Net::HTTP::DigestAuth.new + auth=Net::HTTP::DigestAuth.new.auth_header(uri, + res['www-authenticate'], + req::METHOD) + r.add_field 'Authorization', auth + res = http.request r + end + } + res +end + +def get_json(path) + uri = URI.parse("http://#{@hostname}:#{@port}/nodes#{path}") + res = authenticate(Net::HTTP::Get,uri) + + puts "DEBUG: (g) hostname: #{uri.host}:#{uri.port}" if @debug + puts "DEBUG: (g) request: #{uri.path}" if @debug + puts "DEBUG: (g) return code: #{res.code}" if @debug + puts "DEBUG: (g) return body: #{res.body}" if @debug + + return [res.body, res.code.to_i ] if res.code.to_i != 200 + + struct = JSON.parse(res.body) + + puts "DEBUG: (g) JSON parse structure = #{struct.inspect}" if @debug + + return [struct, 200] +end + +def status() + struct = get_json("/status") + + if struct[1] == 200 + string = "" + struct[0]["nodes"].each do |n, hash| + string += "#{n} #{hash["state"]}\n" + end + [ "#{string}", 0 ] + elsif struct[1] == 404 + [ "No current configuration for status", 1 ] + else + [ "Failed to talk to service show: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def opt_parse() + sub_options = @options.map { |x| x[0] } + lsub_options = @options.map { |x| [ x[0][0], x[2] ] } + opts = GetoptLong.new(*sub_options) + + opts.each do |opt, arg| + case opt + when '--help' + usage 0 + when '--debug' + @debug = true + when '--hostname' + @hostname = arg + when '--username' + @username = arg + when '--password' + @password = arg + when '--port' + @port = arg.to_i + when '--data' + @data = arg + when '--timeout' + @timeout = arg + when '--file' + data = File.read(arg, "r") + else + found = false + lsub_options.each do |x| + next if x[0] != opt + eval x[1] + found = true + end + usage -1 unless found + end + end + + STDERR.puts "CROWBAR_KEY not set, will not be able to authenticate!" if @username.nil? or @password.nil? + STDERR.puts "Please set CROWBAR_KEY or use -U and -P" if @username.nil? or @password.nil? + if ARGV.length == 0 and !@allow_zero_args + usage -1 + end +end + +def run_sub_command(cmds, subcmd) + cmd = cmds[subcmd] + usage -2 if cmd.nil? + eval cmd[0] +end + +def run_command() + run_sub_command(@commands, ARGV.shift) +end + +def main() + opt_parse + res = run_command + puts res[0] + exit res[1] +end + +main + diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_node_status b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_node_status new file mode 100755 index 00000000000..e86cc06d79e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_node_status @@ -0,0 +1,22 @@ +#!/bin/bash + +[[ $CROWBAR_IP ]] || CROWBAR_IP="127.0.0.1" + +stat_line_re='status(OK|WARNING|CRITICAL|UNKNOWN|PENDING)' +echo "Host OK WARN CRIT UNKNOWN PENDING" +while read HOST; do + HOST=${HOST%%.*} + OK=0 WARN=0 CRIT=0 UNKN=0 PEND=0 + while read -r line; do + [[ $line =~ $stat_line_re ]] || continue + case ${BASH_REMATCH[1]} in + OK) ((OK++));; + WARNING) ((WARN++));; + CRITICAL) ((CRIT++));; + UNKNOWN) ((UNKN++));; + PENDING) ((PEND++));; + esac + done < <(wget --user=nagiosadmin --password=password \ + "http://${CROWBAR_IP:=127.0.0.1}/cgi-bin/nagios3/status.cgi?host=$HOST" -O- -q) + echo "$HOST $OK $WARN $CRIT $UNKN $PEND" +done < <(knife node list | grep "\"" | awk -F\" '{ print $2 }') diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_watch_status b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_watch_status new file mode 100755 index 00000000000..c78465529ca --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/crowbar_watch_status @@ -0,0 +1,4 @@ +#!/bin/bash + +watch "/opt/dell/bin/crowbar_node_state status ; echo ' ' ; /opt/dell/bin/crowbar_node_status" + diff --git a/crowbar/change-image/dell/barclamps/crowbar/command_line/transition.sh b/crowbar/change-image/dell/barclamps/crowbar/command_line/transition.sh new file mode 100755 index 00000000000..357e92cc2c3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/command_line/transition.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +key_re='crowbar\.install\.key=([^ ]+)' +if [[ $(cat /proc/cmdline) =~ $key_re ]]; then + export CROWBAR_KEY="${BASH_REMATCH[1]}" +elif [[ -f /etc/crowbar.install.key ]] + export CROWBAR_KEY="$(cat /etc/crowbar.install.key)" +fi + + +post_state() { + local curlargs=(-o "/var/log/$1-$2.json" --connect-timeout 60 -s \ + -L -X POST --data-binary "{ \"name\": \"$1\", \"state\": \"$2\" }" \ + -H "Accept: application/json" -H "Content-Type: application/json") + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) + curl "${curlargs[@]}" "http://192.168.124.10:3000/crowbar/crowbar/1.0/transition/default" +} + +if [ "$1" == "" ] +then + echo "Please specify a node to transition" + exit -1 +fi + +if [ "$2" == "" ] +then + echo "Please specify a state for $1 to transition" + exit -1 +fi + +post_state $1 $2 + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/crowbar/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/compat b/crowbar/change-image/dell/barclamps/crowbar/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/control b/crowbar/change-image/dell/barclamps/crowbar/debian/control new file mode 100644 index 00000000000..cec888c0da6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-crowbar +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-crowbar +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the crowbar barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/copyright b/crowbar/change-image/dell/barclamps/crowbar/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/postinst b/crowbar/change-image/dell/barclamps/crowbar/debian/postinst new file mode 100644 index 00000000000..d8a0a2ae95b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-crowbar/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-crowbar/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-crowbar/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/rules b/crowbar/change-image/dell/barclamps/crowbar/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/crowbar/debian/source/format b/crowbar/change-image/dell/barclamps/crowbar/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/crowbar/version.sh b/crowbar/change-image/dell/barclamps/crowbar/version.sh new file mode 100644 index 00000000000..ee763fcd96c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/crowbar/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=crowbar +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/deployer/Makefile b/crowbar/change-image/dell/barclamps/deployer/Makefile new file mode 100644 index 00000000000..37e37fb0045 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-deployer" + +distclean: + @echo "Dist-Cleaning barclamp-deployer" + +all: clean build install + +build: + @echo "Building barclamp-deployer" + +install: + @echo "Installing barclamp-deployer" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-deployer + cp -r chef ${DESTDIR}/usr/share/barclamp-deployer + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/deployer/app/controllers/deployer_controller.rb b/crowbar/change-image/dell/barclamps/deployer/app/controllers/deployer_controller.rb new file mode 100644 index 00000000000..6afe26cda6e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/app/controllers/deployer_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class DeployerController < BarclampController + def initialize + @service_object = DeployerService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/deployer/app/models/deployer_service.rb b/crowbar/change-image/dell/barclamps/deployer/app/models/deployer_service.rb new file mode 100644 index 00000000000..a93ce626bfe --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/app/models/deployer_service.rb @@ -0,0 +1,276 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class DeployerService < ServiceObject + + def initialize(thelogger) + @bc_name = "deployer" + @logger = thelogger + end + + def create_proposal + @logger.debug("Deployer create_proposal: entering") + base = super + @logger.debug("Deployer create_proposal: leaving") + base + end + + def transition(inst, name, state) + @logger.debug("Deployer transition: entering #{name} for #{state}") + + node = NodeObject.find_node_by_name(name) + if node.nil? + @logger.error("Deployer transition: leaving #{name} for #{state}: Node not found") + return [404, "Failed to find node"] + end + + # + # If we are discovering the node, make sure that we add the deployer client to the node + # + if state == "discovering" + @logger.debug("Deployer transition: leaving #{name} for #{state}: discovering mode") + + db = ProposalObject.find_proposal("deployer", inst) + role = RoleObject.find_role_by_name "deployer-config-#{inst}" + + result = add_role_to_instance_and_node("deployer", inst, name, db, role, "deployer-client") + + @logger.debug("Deployer transition: leaving #{name} for #{state}: discovering mode: #{result}") + return [200, NodeObject.find_node_by_name(name).to_hash ] if result + return [404, "Failed to add role to node"] unless result + end + + # + # The temp booting images need to have clients cleared. + # + if ["delete","discovered","hardware-installed","hardware-updated", + "hardware-installing","hardware-updating","reset","reinstall", + "update"].member?(state) and !node.admin? + @logger.debug("Deployer transition: should be deleting a client entry for #{node.name}") + client = ClientObject.find_client_by_name node.name + @logger.debug("Deployer transition: found and trying to delete a client entry for #{node.name}") unless client.nil? + client.destroy unless client.nil? + + # Make sure that the node can be accessed by knife ssh or ssh + if ["reset","reinstall","update","delete"].member?(state) + system("sudo rm /root/.ssh/known_hosts") + end + end + + # if delete - clear out stuff + if state == "delete" + # Do more work here - one day. + return [200, node.to_hash ] + end + + # + # Decide on the nodes role for the cloud + # * This includes adding a role for node type (for bios/raid update/config) + # * This includes adding an attribute on the node for inclusion in clouds + # + if state == "discovered" + @logger.debug("Deployer transition: discovered state for #{name}") + save_it = false + + if !node.admin? + @logger.debug("Deployer transition: check to see if we should rename: #{name}") + tname = node.name.split(".")[0] + tname = tname.gsub!("h", "d") + new_name = "#{tname}.#{ChefObject.cloud_domain}" + if new_name != node.name + @logger.debug("Deployer transition: renaming node for #{name} #{node.name} -> #{new_name}") + node.destroy + + node.name = new_name + node[:fqdn] = new_name + node[:domain] = ChefObject.cloud_domain + save_it = true + name = new_name + end + else # We are an admin node - display bios updates for now. + node[:bios] ||= {} + node[:bios][:bios_setup_enable] = false + node[:bios][:bios_update_enable] = false + node[:raid] ||= {} + node[:raid][:enable] = false + save_it = true + end + + # + # At this point, we need to create our resource maps and recommendations. + # + # This is hard coded for now. Should be parameter driven one day. + # + @logger.debug("Deployer transition: Update the inventory crowbar structures for #{name}") + node[:crowbar][:disks] = {} if node[:crowbar][:disks].nil? + node[:block_device].each do |disk, data| + # XXX: Make this into a config map one day. + next if disk.start_with?("ram") + next if disk.start_with?("sr") + next if disk.start_with?("loop") + next if disk.start_with?("dm") + next if disk.start_with?("ndb") + next if disk.start_with?("md") + next if disk.start_with?("sg") + next if disk.start_with?("fd") + + next if data[:removable] == 1 or data[:removable] == "1" # Skip cdroms + + # RedHat under KVM reports drives as hdX. Ubuntu reports them as sdX. + disk = disk.gsub("hd", "sd") if disk.start_with?("h") and node[:dmi][:system][:product_name] == "KVM" + + node[:crowbar][:disks][disk] = data + + node[:crowbar][:disks][disk][:usage] = "OS" if disk == "sda" + node[:crowbar][:disks][disk][:usage] = "Storage" unless disk == "sda" + + save_it = true + end unless node[:block_device].nil? or node[:block_device].empty? + + node[:crowbar][:usage] = [] if node[:crowbar][:usage].nil? + if (node[:crowbar][:disks].size > 1) and !node[:crowbar][:usage].include?("swift") + node[:crowbar][:usage] << "swift" + save_it = true + end + + if !node[:crowbar][:usage].include?("nova") + node[:crowbar][:usage] << "nova" + save_it = true + end + + node.save if save_it + + # Allocate required addresses + range = node.admin? ? "admin" : "host" + @logger.debug("Deployer transition: Allocate admin address for #{name}") + ns = NetworkService.new @logger + result = ns.allocate_ip("default", "admin", range, name) + @logger.error("Failed to allocate admin address for: #{node.name}: #{result[0]}") if result[0] != 200 + @logger.debug("Deployer transition: Done Allocate admin address for #{name}") + + @logger.debug("Deployer transition: Allocate bmc address for #{name}") + result = ns.allocate_ip("default", "bmc", "host", name) + @logger.error("Failed to allocate bmc address for: #{node.name}: #{result[0]}") if result[0] != 200 + @logger.debug("Deployer transition: Done Allocate bmc address for #{name}") + + # If we are the admin node, we may need to add a vlan bmc address. + if node.admin? + # Add the vlan bmc if the bmc network and the admin network are not the same. + # not great to do it this way, but hey. + admin_net = ProposalObject.find_data_bag_item "crowbar/admin_network" + bmc_net = ProposalObject.find_data_bag_item "crowbar/bmc_network" + if admin_net["network"]["subnet"] != bmc_net["network"]["subnet"] + @logger.debug("Deployer transition: Allocate bmc_vlan address for #{name}") + result = ns.allocate_ip("default", "bmc_vlan", "host", name) + @logger.error("Failed to allocate bmc_vlan address for: #{node.name}: #{result[0]}") if result[0] != 200 + @logger.debug("Deployer transition: Done Allocate bmc_vlan address for #{name}") + end + end + + # Let it fly to the provisioner. Reload to get the address. + node = NodeObject.find_node_by_name node.name + node[:crowbar][:usedhcp] = true + + role = RoleObject.find_role_by_name "deployer-config-#{inst}" + if role.default_attributes["deployer"]["use_allocate"] and !node.admin? + node.allocated = false + else + node.allocated = true + end + + node.save + + @logger.debug("Deployer transition: leaving discovered for #{name} EOF") + return [200, node.to_hash ] + end + + save_it = false + # + # Once we have been allocated, we will fly through here and we will setup the raid/bios info + # + if state == "hardware-installing" + # build a list of current and pending roles to check against + roles = [] + node["crowbar"]["pending"].each do |k,v| + roles << v + end unless node["crowbar"]["pending"].nil? + roles.flatten! + node.run_list.run_list_items.each do |item| + roles << item.name + end + + # Walk map to categorize the node. Choose first one from the bios map that matches. + role = RoleObject.find_role_by_name "deployer-config-#{inst}" + done = false + role.default_attributes["deployer"]["bios_map"].each do |match| + roles.each do |role| + if role =~ /#{match["pattern"]}/ + node["crowbar"]["hardware"] = {} if node["crowbar"]["hardware"].nil? + node["crowbar"]["hardware"]["bios_set"] = match["bios_set"] if node["crowbar"]["hardware"]["bios_set"].nil? + node["crowbar"]["hardware"]["raid_set"] = match["raid_set"] if node["crowbar"]["hardware"]["raid_set"].nil? + done = true + break + end + end + break if done + end + save_it = true + end + + # + # The node is about to go into update. + # We should make sure that we save and setup the run-list for updating. + # + if state == "update" or state == "hardware-installing" + save_list = node["crowbar"]["save_run_list"] || [] + seen_bios = false + node.run_list.run_list_items.each do |item| + if seen_bios and !(item.name =~ /^ipmi-/) + save_list << item.name + else + seen_bios = true if item.name =~ /^ipmi-/ + end + end + + save_list.each do |item| + node.run_list.run_list_items.delete "role[#{item}]" + end + + node["crowbar"]["save_run_list"] = save_list + save_it = true + end + + # + # Put the run-list back. + # + if state == "hardware-updated" or state == "hardware-installed" + unless node["crowbar"]["save_run_list"].nil? + node["crowbar"]["save_run_list"].each do |name| + node.run_list.run_list_items << "role[#{name}]" + end + end + + node["crowbar"]["save_run_list"] = [] + save_it = true + end + + node.save if save_it + + @logger.debug("Deployer transition: leaving state for #{name} EOF") + return [200, node.to_hash ] + end + +end + diff --git a/crowbar/change-image/dell/barclamps/deployer/barclamp-deployer.spec b/crowbar/change-image/dell/barclamps/deployer/barclamp-deployer.spec new file mode 100644 index 00000000000..18ee13d041c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/barclamp-deployer.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-deployer +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: Crowbar Barclamp to Support Deployments +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages deployer deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-deployer/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-deployer/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-deployer/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/deployer/build_deb.sh b/crowbar/change-image/dell/barclamps/deployer/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/deployer/build_rpm.sh b/crowbar/change-image/dell/barclamps/deployer/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/libraries/barclamp_library.rb b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/libraries/barclamp_library.rb new file mode 100644 index 00000000000..8f50554385b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/libraries/barclamp_library.rb @@ -0,0 +1,100 @@ + +module BarclampLibrary + class Barclamp + class Inventory + def self.list_networks(node) + answer = [] + node[:crowbar][:network].each do |net, data| + answer << Network.new(net, data) + end unless node[:crowbar][:network].nil? + answer + end + + def self.get_network_by_interface(node, intf) + node[:crowbar][:network].each do |net, data| + next if net != intf + return Network.new(net, data) + end unless node[:crowbar][:network].nil? + nil + end + + def self.get_network_by_type(node, type) + node[:crowbar][:network].each do |net, data| + next if data[:usage] != type + return Network.new(net, data) + end unless node[:crowbar][:network].nil? + node[:crowbar][:network].each do |net, data| + next if data[:usage] != "admin" + return Network.new(net, data) + end unless node[:crowbar][:network].nil? + Network.new(type, { "address" => node[:ipaddress] }) + end + + def self.list_disks(node) + answer = [] + node[:crowbar][:disks].each do |disk, data| + answer << Disk.new(disk, data) + end unless node[:crowbar][:disks].nil? + answer + end + + class Network + attr_reader :name, :address, :broadcast, :mac, :netmask, :subnet, :router, :usage, :vlan, :use_vlan, :interface, :interface_list, :add_bridge + def initialize(net, data) + @name = net + @address = data["address"] + @broadcast = data["broadcast"] + @mac = data["mac"] + @netmask = data["netmask"] + @subnet = data["subnet"] + @router = data["router"] + @usage = data["usage"] + @vlan = data["vlan"] + @use_vlan = data["use_vlan"] + @interface = data["interface"] + @interface_list = data["interface_list"] + @add_bridge = data["add_bridge"] + end + end + + class Disk + attr_reader :name, :model, :removable, :rev, :size, :state, :timeout, :vendor, :usage + def initialize(disk, data) + @name = "/dev/#{disk}" + @model = data["model"] || "Unknown" + @removable = data["removable"] != "0" + @rev = data["rev"] || "Unknown" + @size = (data["size"] || 0).to_i + @state = data["state"] || "Unknown" + @timeout = (data["timeout"] || 0).to_i + @vendor = data["vendor"] || "NA" + @usage = data["usage"] || "Unknown" + end + + def self.size_to_bytes(s) + case s + when /^([0-9]+)$/ + return $1.to_f + + when /^([0-9]+)[Kk][Bb]$/ + return $1.to_f * 1024 + + when /^([0-9]+)[Mm][Bb]$/ + return $1.to_f * 1024 * 1024 + + when /^([0-9]+)[Gg][Bb]$/ + return $1.to_f * 1024 * 1024 * 1024 + + when /^([0-9]+)[Tt][Bb]$/ + return $1.to_f * 1024 * 1024 * 1024 * 1024 + end + -1 + end + + end + + end + end +end + + diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/metadata.json b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/metadata.json new file mode 100644 index 00000000000..2008778432e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "barclamp": "Library for Barclamp functions" + }, + "maintainer_email": "crowbar@dell.com", + "name": "barclamp", + "conflicting": { + }, + "description": "Library for Barclamp functions", + "long_description": "" + } diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/recipes/default.rb b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/recipes/default.rb new file mode 100644 index 00000000000..4545dd8b1f9 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/barclamp/recipes/default.rb @@ -0,0 +1,5 @@ + +class Chef::Recipe + include BarclampLibrary +end + diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/README.md b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/README.md new file mode 100644 index 00000000000..94f24e10632 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/README.md @@ -0,0 +1,41 @@ +Description +=========== + +Creates a configured plugin path for distributing custom Ohai plugins, and reloads them via Ohai within the context of a Chef Client run during the compile phase. + +Attributes +========== + +`node[:ohai][:plugin_path]` - location to drop off plugins directory, default is `/etc/chef/ohai_plugins`. This is not FHS-compliant, an FHS location would be something like `/var/lib/ohai/plugins`, or `/var/lib/chef/ohai_plugins` or similar. + +Neither an FHS location or the default value of this attribute are in the default Ohai plugin path. Set the Ohai plugin path with the config setting "`Ohai::Config[:plugin_path`" in the Chef config file. The attribute is not set to the default plugin path that Ohai ships with because we don't want to risk destroying existing essential plugins for Ohai. + +Usage +===== + +Put the recipe `ohai` at the start of the node's run list to make sure that custom plugins are loaded early on in the Chef run and data is available for later recipes. + +The execution of the custom plugins occurs within the recipe during the compile phase, so you can write new plugins and use the data they return in your Chef recipes. + +For information on how to write custom plugins for Ohai, please see the Chef wiki pages. + +http://wiki.opscode.com/display/chef/Writing+Ohai+Plugins + +License and Author +================== + +Author:: Joshua Timberman () + +Copyright:: 2010, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/attributes/default.rb b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/attributes/default.rb new file mode 100644 index 00000000000..7e2b1585f07 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/attributes/default.rb @@ -0,0 +1,21 @@ +# +# Cookbook Name:: ohai +# Attribute:: default +# +# Copyright 2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# FHS location would be /var/lib/chef/ohai_plugins or similar. +default[:ohai][:plugin_path] = "/etc/chef/ohai_plugins" diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/files/default/plugins/README b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/files/default/plugins/README new file mode 100644 index 00000000000..72f12e3644f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/files/default/plugins/README @@ -0,0 +1 @@ +This directory contains custom plugins for Ohai. diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/files/default/plugins/crowbar.rb b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/files/default/plugins/crowbar.rb new file mode 100644 index 00000000000..52dca060737 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/files/default/plugins/crowbar.rb @@ -0,0 +1,104 @@ + +require 'timeout' + +provides "crowbar" + +class System + def self.background_time_command(timeout, background, name, command) + File.open("/tmp/tcpdump-#{name}.sh", "w+") { |fd| + fd.puts("#!/bin/bash") + fd.puts("#{command} &") + fd.puts("sleep #{timeout}") + fd.puts("kill %1") + } + + system("chmod +x /tmp/tcpdump-#{name}.sh") + if background + system("/tmp/tcpdump-#{name}.sh &") + else + system("/tmp/tcpdump-#{name}.sh") + end + end +end + +filename = "/usr/sbin/lshw" +if !File.exists?(filename) + filename = "/usr/bin/lshw" +end +f = IO.popen("#{filename} -class network | egrep 'logical|bus info|-network|serial'") +networks = [] +mac_map = {} +bus_found=false +logical_name="" +mac_addr="" +wait=false +f.each { |line| + if line =~ /\*-network/ + if bus_found + networks << logical_name + mac_map[logical_name] = mac_addr + if !File.exists?("/tmp/tcpdump.#{logical_name}.out") + System.background_time_command(45, true, logical_name, "ifconfig #{logical_name} up ; tcpdump -c 1 -lv -v -i #{logical_name} -a -e -s 1514 ether proto 0x88cc > /tmp/tcpdump.#{logical_name}.out") + wait=true + end + end + bus_found=false + end + if line =~ /bus info:/ + bus_found = true + end + if line =~ /logical name: (.*)/ + logical_name = $1 + end + if line =~ /serial: (.*)/ + mac_addr = $1 + end +} +f.close +if bus_found + networks << logical_name + mac_map[logical_name] = mac_addr + if !File.exists?("/tmp/tcpdump.#{logical_name}.out") + System.background_time_command(45, true, logical_name, "ifconfig #{logical_name} up ; tcpdump -c 1 -lv -v -i #{logical_name} -a -e -s 1514 ether proto 0x88cc > /tmp/tcpdump.#{logical_name}.out") + wait=true + end +end +system("sleep 45") if wait + +crowbar Mash.new +crowbar[:switch_config] = Mash.new unless crowbar[:switch_config] + +networks.each do |network| + sw_port = -1 + line = %x[cat /tmp/tcpdump.#{network}.out | grep "Subtype Interface Name"] + if line =~ /[\d]+\/[\d]+\/([\d]+)/ + sw_port = $1 + end + if line =~ /: Unit [\d]+ Port ([\d]+)/ + sw_port = $1 + end + + sw_unit = -1 + line = %x[cat /tmp/tcpdump.#{network}.out | grep "Subtype Interface Name"] + if line =~ /([\d]+)\/[\d]+\/[\d]+/ + sw_unit = $1 + end + if line =~ /: Unit ([\d]+) Port [\d]+/ + sw_unit = $1 + end + + sw_name = -1 + # GREG: Using mac for now, but should change to something else later. + line = %x[cat /tmp/tcpdump.#{network}.out | grep "Subtype MAC address"] + if line =~ /: (.*) \(oui/ + sw_name = $1 + end + + crowbar[:switch_config][network] = Mash.new unless crowbar[:switch_config][network] + crowbar[:switch_config][network][:interface] = network + crowbar[:switch_config][network][:mac] = mac_map[network].downcase + crowbar[:switch_config][network][:switch_name] = sw_name + crowbar[:switch_config][network][:switch_port] = sw_port + crowbar[:switch_config][network][:switch_unit] = sw_unit +end + diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/metadata.json b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/metadata.json new file mode 100644 index 00000000000..25444416d92 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/metadata.json @@ -0,0 +1,30 @@ +{ + "name": "ohai", + "description": "Distributes a directory of custom ohai plugins", + "long_description": "Description\n===========\n\nCreates a configured plugin path for distributing custom Ohai plugins, and reloads them via Ohai within the context of a Chef Client run during the compile phase.\n\nAttributes\n==========\n\n`node[:ohai][:plugin_path]` - location to drop off plugins directory, default is `/etc/chef/ohai_plugins`. This is not FHS-compliant, an FHS location would be something like `/var/lib/ohai/plugins`, or `/var/lib/chef/ohai_plugins` or similar.\n\nNeither an FHS location or the default value of this attribute are in the default Ohai plugin path. Set the Ohai plugin path with the config setting \"`Ohai::Config[:plugin_path`\" in the Chef config file. The attribute is not set to the default plugin path that Ohai ships with because we don't want to risk destroying existing essential plugins for Ohai.\n\nUsage\n=====\n\nPut the recipe `ohai` at the start of the node's run list to make sure that custom plugins are loaded early on in the Chef run and data is available for later recipes.\n\nThe execution of the custom plugins occurs within the recipe during the compile phase, so you can write new plugins and use the data they return in your Chef recipes.\n\nFor information on how to write custom plugins for Ohai, please see the Chef wiki pages.\n\nhttp://wiki.opscode.com/display/chef/Writing+Ohai+Plugins\n\nLicense and Author\n==================\n\nAuthor:: Joshua Timberman ()\n\nCopyright:: 2010, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "maintainer": "Opscode, Inc", + "maintainer_email": "cookbooks@opscode.com", + "license": "Apache 2.0", + "platforms": { + }, + "dependencies": { + }, + "recommendations": { + }, + "suggestions": { + }, + "conflicting": { + }, + "providing": { + }, + "replacing": { + }, + "attributes": { + }, + "groupings": { + }, + "recipes": { + "ohai::default": "Distributes a directory of custom ohai plugins" + }, + "version": "0.9.0" +} \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/metadata.rb b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/metadata.rb new file mode 100644 index 00000000000..02f52da3d45 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/metadata.rb @@ -0,0 +1,7 @@ +maintainer "Opscode, Inc" +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Distributes a directory of custom ohai plugins" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.9.0" +recipe "ohai::default", "Distributes a directory of custom ohai plugins" diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/recipes/default.rb b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/recipes/default.rb new file mode 100644 index 00000000000..a1392e6d4bb --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/cookbooks/ohai/recipes/default.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: ohai +# Recipe:: default +# +# Copyright 2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +Ohai::Config[:plugin_path] << node.ohai.plugin_path +Chef::Log.info("ohai plugins will be at: #{node.ohai.plugin_path}") + +d = directory node.ohai.plugin_path do + owner 'root' + group 'root' + mode 0755 + recursive true + action :nothing +end + +d.run_action(:create) + +rd = remote_directory node.ohai.plugin_path do + source 'plugins' + owner 'root' + group 'root' + mode 0755 + action :nothing +end + +rd.run_action(:create) + +o = Ohai::System.new +o.all_plugins +node.automatic_attrs.merge! o.data diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/data_bags/crowbar/bc-template-deployer.json b/crowbar/change-image/dell/barclamps/deployer/chef/data_bags/crowbar/bc-template-deployer.json new file mode 100644 index 00000000000..523d0f3ffa2 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/data_bags/crowbar/bc-template-deployer.json @@ -0,0 +1,30 @@ +{ + "id": "bc-template-deployer", + "description": "Initial classification system for the Crowbar environment ", + "attributes": { + "deployer": { + "use_allocate": true, + "bios_map": [ + { "pattern": "nova-.*", "bios_set": "Virtualization", "raid_set": "SingleRaid10" }, + { "pattern": "swift-.*", "bios_set": "Storage", "raid_set": "JBODOnly" }, + { "pattern": ".*", "bios_set": "Virtualization", "raid_set": "SingleRaid10" } + ] + } + }, + "deployment": { + "deployer": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "deployer-client" ] + ], + "config": { + "environment": "deployer-config-test", + "mode": "full", + "transitions": true, + "transition_list": [ "all" ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/data_bags/crowbar/bc-template-deployer.schema b/crowbar/change-image/dell/barclamps/deployer/chef/data_bags/crowbar/bc-template-deployer.schema new file mode 100644 index 00000000000..0db33d463f7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/data_bags/crowbar/bc-template-deployer.schema @@ -0,0 +1,82 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-deployer-|^bc-template-deployer$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "deployer": { + "type": "map", + "required": true, + "mapping": { + "use_allocate": { "type": "bool", "required": true }, + "bios_map": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "map", + "required": true, + "mapping": { + "pattern": { "type": "str", "required": true }, + "bios_set": { "type": "str", "required": true }, + "raid_set": { "type": "str", "required": true } + } + } ] + } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "deployer": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/deployer/chef/roles/deployer-client.rb b/crowbar/change-image/dell/barclamps/deployer/chef/roles/deployer-client.rb new file mode 100644 index 00000000000..83884dfc4ba --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/chef/roles/deployer-client.rb @@ -0,0 +1,10 @@ + +name "deployer-client" +description "Deployer Client role - Discovery components" +run_list( + "recipe[ohai]", + "recipe[barclamp]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/deployer/command_line/crowbar_deployer b/crowbar/change-image/dell/barclamps/deployer/command_line/crowbar_deployer new file mode 100755 index 00000000000..8b8d95b0823 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/command_line/crowbar_deployer @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "deployer" + +main diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/deployer/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/compat b/crowbar/change-image/dell/barclamps/deployer/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/control b/crowbar/change-image/dell/barclamps/deployer/debian/control new file mode 100644 index 00000000000..a0ef4472fb8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-deployer +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-deployer +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the deployer barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/copyright b/crowbar/change-image/dell/barclamps/deployer/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/postinst b/crowbar/change-image/dell/barclamps/deployer/debian/postinst new file mode 100644 index 00000000000..e1c58a3fc6c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-deployer/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-deployer/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-deployer/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/rules b/crowbar/change-image/dell/barclamps/deployer/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/deployer/debian/source/format b/crowbar/change-image/dell/barclamps/deployer/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/deployer/version.sh b/crowbar/change-image/dell/barclamps/deployer/version.sh new file mode 100644 index 00000000000..6cc48bef016 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/deployer/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=deployer +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/dns/Makefile b/crowbar/change-image/dell/barclamps/dns/Makefile new file mode 100644 index 00000000000..ceaf3b5ce81 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-dns" + +distclean: + @echo "Dist-Cleaning barclamp-dns" + +all: clean build install + +build: + @echo "Building barclamp-dns" + +install: + @echo "Installing barclamp-dns" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-dns + cp -r chef ${DESTDIR}/usr/share/barclamp-dns + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/dns/app/controllers/dns_controller.rb b/crowbar/change-image/dell/barclamps/dns/app/controllers/dns_controller.rb new file mode 100644 index 00000000000..963fc1a61c5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/app/controllers/dns_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class DnsController < BarclampController + def initialize + @service_object = DnsService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/dns/app/models/dns_service.rb b/crowbar/change-image/dell/barclamps/dns/app/models/dns_service.rb new file mode 100644 index 00000000000..28f539d61b1 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/app/models/dns_service.rb @@ -0,0 +1,62 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class DnsService < ServiceObject + + def initialize(thelogger) + @bc_name = "dns" + @logger = thelogger + end + + def create_proposal + @logger.debug("DNS create_proposal: entering") + base = super + @logger.debug("DNS create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("DNS transition: entering for #{name} for #{state}") + + # + # If we are discovering the node, make sure that we add the dns client or server to the node + # + if state == "discovered" + @logger.debug("DNS transition: handling for #{name} for #{state}: discovered") + db = ProposalObject.find_proposal "dns", inst + role = RoleObject.find_role_by_name "dns-config-#{inst}" + + if role.override_attributes["dns"]["elements"]["dns-server"].nil? or + role.override_attributes["dns"]["elements"]["dns-server"].empty? + @logger.debug("DNS transition: adding #{name} to dns-server role") + result = add_role_to_instance_and_node("dns", inst, name, db, role, "dns-server") + end + + # Always add the dns client + @logger.debug("DNS transition: adding #{name} to dns-client role") + result = add_role_to_instance_and_node("dns", inst, name, db, role, "dns-client") + + a = [200, {}] if result + a = [400, "Failed to add role to node"] unless result + @logger.debug("DNS transition: leaving for #{name} for #{state}: discovered") + return a + end + + @logger.debug("DNS transition: leaving for #{name} for #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + + +end diff --git a/crowbar/change-image/dell/barclamps/dns/barclamp-dns.spec b/crowbar/change-image/dell/barclamps/dns/barclamp-dns.spec new file mode 100644 index 00000000000..1b96bf11bc8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/barclamp-dns.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-dns +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: manages the DNS subsystem for the cluste +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages dns deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-dns/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-dns/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-dns/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/dns/build_deb.sh b/crowbar/change-image/dell/barclamps/dns/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/dns/build_rpm.sh b/crowbar/change-image/dell/barclamps/dns/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/README.rdoc b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/README.rdoc new file mode 100644 index 00000000000..bddd59c4be5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/README.rdoc @@ -0,0 +1,51 @@ += DESCRIPTION: + + += REQUIREMENTS: + +== Platform: + + +== Cookbooks: + + +* ruby +* apache2 +* passenger + += ATTRIBUTES: + +* rails[:version] - Install the specified version. Default false (installs latest). +* rails[:environment] - Set Rails environment. Default production. + += USAGE: + +The recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define. + + include_recipe "apache2" + include_recipe "passenger" + include_recipe "rails" + + web_app "some_rails_app" do + docroot "/srv/some_rails_app/public" + template "some_rails_app.conf.erb" + end + +We provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/attributes/default.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/attributes/default.rb new file mode 100644 index 00000000000..98533aa2f04 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/attributes/default.rb @@ -0,0 +1,5 @@ + +default[:dns][:forwarders] = [ + "143.166.33.44", + "143.166.220.125" ] + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/metadata.json b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/metadata.json new file mode 100644 index 00000000000..a66480df675 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/metadata.json @@ -0,0 +1,65 @@ +{ + "providing": { + }, + "attributes": { + "dns": { + "display_name": "DNS Hash", + "description": "Hash of DNS attributes", + "type": "hash", + "choice": [ + + ], + "calculated": false, + "required": "optional", + "recipes": [ + + ] + }, + "dns/forwarders": { + "display_name": "Forwarders", + "description": "List of IPs for the DNS server to forward requests", + "type": "array", + "default": [ + + ], + "choice": [ + + ], + "calculated": false, + "required": "optional", + "recipes": [ + + ] + } + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "bind9": "Installs bind9" + }, + "maintainer_email": "crowbar@dell.com", + "name": "bind9", + "conflicting": { + }, + "description": "Installs bind9", + "long_description": "" +} diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/providers/host.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/providers/host.rb new file mode 100644 index 00000000000..f30070edb8c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/providers/host.rb @@ -0,0 +1,18 @@ + + +action :add do + utils_line "#{new_resource.ipaddress} #{new_resource.hostname}" do + action :add + file "/etc/bind/hosts" + notifies :run, resources(:bash => "build-domain-file"), :delayed + end +end + +action :remove do + utils_line "#{new_resource.ipaddress} #{new_resource.hostname}" do + action :remove + file "/etc/bind/hosts" + notifies :run, resources(:bash => "build-domain-file"), :delayed + end +end + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/providers/net.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/providers/net.rb new file mode 100644 index 00000000000..67003bc7976 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/providers/net.rb @@ -0,0 +1,18 @@ + + +action :add do + utils_line "-n #{new_resource.subnet} -N #{new_resource.netmask}" do + action :add + file "/etc/bind/netargs" + notifies :run, resources(:bash => "build-domain-file"), :delayed + end +end + +action :remove do + utils_line "-n #{new_resource.subnet} -N #{new_resource.netmask}" do + action :remove + file "/etc/bind/netargs" + notifies :run, resources(:bash => "build-domain-file"), :delayed + end +end + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/recipes/default.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/recipes/default.rb new file mode 100644 index 00000000000..0b1cf91965e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/recipes/default.rb @@ -0,0 +1,94 @@ + +include_recipe "utils" + +package "bind9" +package "bind9utils" + +template "/etc/bind/named.conf.options" do + source "named.conf.options.erb" + variables(:forwarders => node[:dns][:forwarders]) + mode 0644 + owner "root" + group "bind" + notifies :restart, "service[bind9]" +end + +service "bind9" do + supports :restart => true, :status => true, :reload => true + running true + enabled true + action :enable +end + +file "/etc/bind/hosts" do + owner "root" + group "root" + mode 0644 + content "" + action :create + not_if do File.exists?("/etc/bind/hosts") end +end + +file "/etc/bind/netargs" do + owner "root" + group "root" + mode 0644 + content "" + action :create + not_if do File.exists?("/etc/bind/netargs") end +end + +bash "build-domain-file" do + code <<-EOH + mkdir /tmp/tmp.$$ + cd /tmp/tmp.$$ + + NET_ARGS=`cat /etc/bind/netargs | while read line + do + echo -n "$line " + done` + + /opt/dell/bin/h2n -d #{node[:dns][:domain]} -u #{node[:dns][:contact]} $NET_ARGS -H /etc/bind/hosts -h localhost +c named.conf.local -q + rm -f boot.cacheonly conf.cacheonly db.127.0.0 named.boot dns.hosts + sed -i 's/"db/"\\/etc\\/bind\\/db/' named.conf.local + grep zone named.conf.local | grep -v "zone \\".\\"" | grep -v "0.0.127" > named.conf.new + mv named.conf.new named.conf.local + cp * /etc/bind + + rm -rf /tmp/tmp.$$ +EOH + action :nothing + notifies :restart, resources(:service => "bind9"), :immediately +end + +# +# This relies on the network barclamp networks - GREG: Not sure I like this! +# +storage_network = data_bag_item('crowbar', 'storage_network') +admin_network = data_bag_item('crowbar', 'admin_network') +bmc_network = data_bag_item('crowbar', 'bmc_network') + +[ admin_network, bmc_network, storage_network ].each do |network| + network_name = network[:id].gsub("_network","") + base_name = "" + network[:allocated].each do |ip,h| + base_name = "#{h[:machine]} " if network_name == "admin" + hostname_str = "#{base_name}#{network_name}.#{h[:machine]}" + bind9_host ip do + hostname hostname_str + action :add + end + end + bind9_net network[:network][:subnet] do + netmask network[:network][:netmask] + action :add + end +end + +node[:dns][:static].each do |name,ip| + bind9_host ip do + hostname name + action :add + end +end + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/resources/host.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/resources/host.rb new file mode 100644 index 00000000000..2819a417eea --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/resources/host.rb @@ -0,0 +1,6 @@ + +actions :add, :remove + +attribute :ipaddress, :kind_of => String, :name_attribute => true +attribute :hostname, :kind_of => String + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/resources/net.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/resources/net.rb new file mode 100644 index 00000000000..06208fddac1 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/resources/net.rb @@ -0,0 +1,6 @@ + +actions :add, :remove + +attribute :subnet, :kind_of => String, :name_attribute => true +attribute :netmask, :kind_of => String + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/templates/default/named.conf.options.erb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/templates/default/named.conf.options.erb new file mode 100644 index 00000000000..8c945f296f2 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/bind9/templates/default/named.conf.options.erb @@ -0,0 +1,24 @@ +options { + directory "/var/cache/bind"; + + // If there is a firewall between you and nameservers you want + // to talk to, you may need to fix the firewall to allow multiple + // ports to talk. See http://www.kb.cert.org/vuls/id/800113 + + // If your ISP provided one or more IP addresses for stable + // nameservers, you probably want to use them as forwarders. + // Uncomment the following block, and insert the addresses replacing + // the all-0's placeholder. + + forwarders { +<% if !@forwarders.nil? and !@forwarders.empty? -%> +<% @forwarders.each do |i| -%> + <%= i %>; +<% end -%> +<% end -%> + }; + + auth-nxdomain no; # conform to RFC1035 + listen-on-v6 { any; }; +}; + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/README.md b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/README.md new file mode 100644 index 00000000000..28affb876ff --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/README.md @@ -0,0 +1,40 @@ +DESCRIPTION +=========== + +Configures /etc/resolv.conf. + +USAGE +===== + +Set the resolver attributes in a role, for example from my base.rb: + + "resolver" => { + "nameservers" => ["10.13.37.120", "10.13.37.40"], + "search" => "int.example.org" + } + +The resulting /etc/resolv.conf will look like: + + domain int.example.org + search int.example.org + nameserver 10.13.37.120 + nameserver 10.13.37.40 + +LICENSE AND AUTHOR +================== + +Author:: Joshua Timberman () + +Copyright 2009, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/attributes/default.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/attributes/default.rb new file mode 100644 index 00000000000..361e18b0c8d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/attributes/default.rb @@ -0,0 +1,2 @@ +default[:dns][:domain] = domain +default[:dns][:nameservers] = [ ] diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/metadata.json b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/metadata.json new file mode 100644 index 00000000000..542f14d72ed --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/metadata.json @@ -0,0 +1,97 @@ +{ + "providing": { + }, + "attributes": { + "dns/nameservers": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": [ + "" + ], + "type": "array", + "recipes": [ + + ], + "description": "Default nameservers", + "display_name": "Resolver Nameservers" + }, + "dns/domain": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "domain", + "type": "string", + "recipes": [ + + ], + "description": "Default domain to domain", + "display_name": "Resolver Search" + }, + "dns": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "type": "hash", + "recipes": [ + + ], + "description": "Hash of Resolver attributes", + "display_name": "Resolver" + } + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "openbsd": [ + + ], + "debian": [ + + ], + "centos": [ + + ], + "fedora": [ + + ], + "macosx": [ + + ], + "freebsd": [ + + ], + "ubuntu": [ + + ], + "redhat": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.8.2", + "maintainer": "Opscode, Inc.", + "suggestions": { + }, + "recipes": { + "resolver": "Configures /etc/resolv.conf via attributes" + }, + "maintainer_email": "cookbooks@opscode.com", + "name": "resolver", + "conflicting": { + }, + "description": "Configures /etc/resolv.conf", + "long_description": "DESCRIPTION\n===========\n\nConfigures /etc/resolv.conf.\n\nUSAGE\n=====\n\nSet the resolver attributes in a role, for example from my base.rb:\n\n \"resolver\" => {\n \"nameservers\" => [\"10.13.37.120\", \"10.13.37.40\"],\n \"domain\" => \"int.example.org\"\n }\n\nThe resulting /etc/resolv.conf will look like:\n\n domain int.example.org\n search int.example.org\n nameserver 10.13.37.120\n nameserver 10.13.37.40\n\nLICENSE AND AUTHOR\n==================\n\nAuthor:: Joshua Timberman ()\n\nCopyright 2009, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n" + } diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/metadata.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/metadata.rb new file mode 100644 index 00000000000..0a4185281a0 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/metadata.rb @@ -0,0 +1,29 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures /etc/resolv.conf" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.8.2" + +recipe "resolver", "Configures /etc/resolv.conf via attributes" + +%w{ ubuntu debian fedora centos redhat freebsd openbsd macosx }.each do |os| + supports os +end + +attribute "resolver", + :display_name => "Resolver", + :description => "Hash of Resolver attributes", + :type => "hash" + +attribute "resolver/domain", + :display_name => "Resolver Search", + :description => "Default domain to domain", + :default => "domain" + +attribute "resolver/nameservers", + :display_name => "Resolver Nameservers", + :description => "Default nameservers", + :type => "array", + :default => [""] + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/recipes/default.rb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/recipes/default.rb new file mode 100644 index 00000000000..84f6a9c030d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/recipes/default.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: resolver +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +env_filter = " AND dns_config_environment:#{node[:dns][:config][:environment]}" +nodes = search(:node, "roles:dns-server#{env_filter}") + +dns_list = [] +if !nodes.nil? and !nodes.empty? + dns_list = nodes.map { |x| Chef::Recipe::Barclamp::Inventory.get_network_by_type(x, "admin").address } +end +dns_list << node[:dns][:nameservers] + +template "/etc/resolv.conf" do + source "resolv.conf.erb" + owner "root" + group "root" + mode 0644 + variables(:nameservers => dns_list.flatten, :search => node[:dns][:domain]) +end diff --git a/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/templates/default/resolv.conf.erb b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/templates/default/resolv.conf.erb new file mode 100644 index 00000000000..4ff74863206 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/cookbooks/resolver/templates/default/resolv.conf.erb @@ -0,0 +1,6 @@ +<% unless @search.nil? -%> +search <%= @search %> +<% end -%> +<% @nameservers.each do |nameserver| -%> +nameserver <%= nameserver %> +<% end -%> diff --git a/crowbar/change-image/dell/barclamps/dns/chef/data_bags/crowbar/bc-template-dns.json b/crowbar/change-image/dell/barclamps/dns/chef/data_bags/crowbar/bc-template-dns.json new file mode 100644 index 00000000000..aeef6a22845 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/data_bags/crowbar/bc-template-dns.json @@ -0,0 +1,31 @@ +{ + "id": "bc-template-dns", + "description": "manages the DNS subsystem for the cluster", + "attributes": { + "dns": { + "domain": "pod.cloud.openstack.org", + "contact": "support@pod.cloud.openstack.org", + "forwarders": [ ], + "static": { } + } + }, + "deployment": { + "dns": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "dns-server" ], + [ "dns-client" ] + ], + "config": { + "environment": "dns-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/data_bags/crowbar/bc-template-dns.schema b/crowbar/change-image/dell/barclamps/dns/chef/data_bags/crowbar/bc-template-dns.schema new file mode 100644 index 00000000000..749d02741f8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/data_bags/crowbar/bc-template-dns.schema @@ -0,0 +1,82 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-dns-|^bc-template-dns$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "dns": { + "type": "map", + "required": true, + "mapping": { + "domain": { "type": "str", "required": true, "name": "DomainName" }, + "contact": { "type": "str", "required": true, "name": "Email" }, + "forwarders": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str", "name": "IpAddress" } ] + }, + "static": { + "type": "map", + "required": true, + "mapping": { + = : { "type": "str", "name": "IpAddress" } + } + } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "dns": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/dns/chef/roles/dns-client.rb b/crowbar/change-image/dell/barclamps/dns/chef/roles/dns-client.rb new file mode 100644 index 00000000000..4b3fe0c3a81 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/roles/dns-client.rb @@ -0,0 +1,9 @@ + +name "dns-client" +description "DNS Client Role - Configures the resolver to point at the DNS server" +run_list( + "recipe[resolver]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/dns/chef/roles/dns-server.rb b/crowbar/change-image/dell/barclamps/dns/chef/roles/dns-server.rb new file mode 100644 index 00000000000..8138c1ddf3e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/chef/roles/dns-server.rb @@ -0,0 +1,9 @@ + +name "dns-server" +description "DNS Server Role - DNS server for the cloud" +run_list( + "recipe[bind9]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/dns/command_line/crowbar_dns b/crowbar/change-image/dell/barclamps/dns/command_line/crowbar_dns new file mode 100755 index 00000000000..54371ad949d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/command_line/crowbar_dns @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "dns" + +main diff --git a/crowbar/change-image/dell/barclamps/dns/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/dns/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/dns/debian/compat b/crowbar/change-image/dell/barclamps/dns/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/dns/debian/control b/crowbar/change-image/dell/barclamps/dns/debian/control new file mode 100644 index 00000000000..31e03ce8795 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-dns +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-dns +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the dns barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/dns/debian/copyright b/crowbar/change-image/dell/barclamps/dns/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/dns/debian/postinst b/crowbar/change-image/dell/barclamps/dns/debian/postinst new file mode 100644 index 00000000000..8a5dfb14566 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-dns/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-dns/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-dns/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/dns/debian/rules b/crowbar/change-image/dell/barclamps/dns/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/dns/debian/source/format b/crowbar/change-image/dell/barclamps/dns/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/dns/version.sh b/crowbar/change-image/dell/barclamps/dns/version.sh new file mode 100644 index 00000000000..471ba66571e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/dns/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=dns +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/ganglia/Makefile b/crowbar/change-image/dell/barclamps/ganglia/Makefile new file mode 100644 index 00000000000..d722202875c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-ganglia" + +distclean: + @echo "Dist-Cleaning barclamp-ganglia" + +all: clean build install + +build: + @echo "Building barclamp-ganglia" + +install: + @echo "Installing barclamp-ganglia" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-ganglia + cp -r chef ${DESTDIR}/usr/share/barclamp-ganglia + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/ganglia/app/controllers/ganglia_controller.rb b/crowbar/change-image/dell/barclamps/ganglia/app/controllers/ganglia_controller.rb new file mode 100644 index 00000000000..da56bb29b30 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/app/controllers/ganglia_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class GangliaController < BarclampController + def initialize + @service_object = GangliaService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/ganglia/app/models/ganglia_service.rb b/crowbar/change-image/dell/barclamps/ganglia/app/models/ganglia_service.rb new file mode 100644 index 00000000000..b5d8d5a5561 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/app/models/ganglia_service.rb @@ -0,0 +1,89 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class GangliaService < ServiceObject + + def initialize(thelogger) + @bc_name = "ganglia" + @logger = thelogger + end + + def create_proposal + @logger.debug("Ganglia create_proposal: entering") + base = super + @logger.debug("Ganglia create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("Ganglia transition: make sure that network role is on all nodes: #{name} for #{state}") + + # + # If we are discovering the node, make sure that we add the ganglia client or server to the node + # + if state == "discovered" + @logger.debug("Ganglia transition: discovered state for #{name} for #{state}") + db = ProposalObject.find_proposal "ganglia", inst + role = RoleObject.find_role_by_name "ganglia-config-#{inst}" + + if role.override_attributes["ganglia"]["elements"]["ganglia-server"].nil? or + role.override_attributes["ganglia"]["elements"]["ganglia-server"].empty? + @logger.debug("Ganglia transition: make sure that ganglia-server role is on first: #{name} for #{state}") + result = add_role_to_instance_and_node("ganglia", inst, name, db, role, "ganglia-server") + else + node = NodeObject.find_node_by_name name + unless node.role? "ganglia-server" + @logger.debug("Ganglia transition: make sure that ganglia-client role is on all nodes but first: #{name} for #{state}") + result = add_role_to_instance_and_node("ganglia", inst, name, db, role, "ganglia-client") + end + end + + # Set up the client url + if result + role = RoleObject.find_role_by_name "ganglia-config-#{inst}" + + # Get the server IP address + server_ip = nil + [ "ganglia-server" ].each do |element| + tnodes = role.override_attributes["ganglia"]["elements"][element] + next if tnodes.nil? or tnodes.empty? + tnodes.each do |n| + next if n.nil? + node = NodeObject.find_node_by_name(n) + server_ip = node.get_network_by_type("admin")["address"] + end + end + + unless server_ip.nil? + node = NodeObject.find_node_by_name(name) + node["crowbar"] = {} if node["crowbar"].nil? + node["crowbar"]["links"] = {} if node["crowbar"]["links"].nil? + node["crowbar"]["links"]["Ganglia"] = "http://#{server_ip}/ganglia/?c=Crowbar PoC&h=#{node.name}&m=load_one&r=hour&s=descending&hc=4&mc=2" + node.save + end + end + + @logger.debug("Ganglia transition: leaving from discovered state for #{name} for #{state}") + a = [200, NodeObject.find_node_by_name(name).to_hash ] if result + a = [400, "Failed to add role to node"] unless result + return a + end + + @logger.debug("Ganglia transition: leaving for #{name} for #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + +end + diff --git a/crowbar/change-image/dell/barclamps/ganglia/barclamp-ganglia.spec b/crowbar/change-image/dell/barclamps/ganglia/barclamp-ganglia.spec new file mode 100644 index 00000000000..fb0825a2421 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/barclamp-ganglia.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-ganglia +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: Crowbar Barclamp to Support Ganglia +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages ganglia deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-ganglia/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-ganglia/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-ganglia/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/ganglia/build_deb.sh b/crowbar/change-image/dell/barclamps/ganglia/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/ganglia/build_rpm.sh b/crowbar/change-image/dell/barclamps/ganglia/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/attributes/default.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/attributes/default.rb new file mode 100644 index 00000000000..b1e9841670c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/attributes/default.rb @@ -0,0 +1,5 @@ +# +# Default attributes for system. +# +default[:ganglia][:interface_eval] = '#{"eth0"}' + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/libraries/eval.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/libraries/eval.rb new file mode 100644 index 00000000000..80e4809879d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/libraries/eval.rb @@ -0,0 +1,43 @@ +# +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: andi abes +# + +class Ganglia +class Evaluator + + def initialize(node) + @b = binding + end + + def eval_with_context(str) + eval(str,@b) + end + + def log_eval_vars() + eval("Chef::Log.info('locals:'+local_variables.join(':') + '\nglobals:'+global_variables.join(':'))") + end + + def self.get_value_by_type(node, type) + location = node[:ganglia][type] + e = Evaluator.new(node) + val = e.eval_with_context(location) + Chef::Log.info("Looking at #{location} for #{type}. Got: #{val}") + val + end + +end +end diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/metadata.json b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/metadata.json new file mode 100644 index 00000000000..a65b2ce6db3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/metadata.json @@ -0,0 +1,38 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "ganglia": "Ganglia", + "ganglia::server": "Ganglia Server", + "ganglia::client": "Ganglia Monitoring Client" + }, + "maintainer_email": "crowbar@dell.com", + "name": "ganglia", + "conflicting": { + }, + "description": "Installs ganglia", + "long_description": "" +} diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/client.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/client.rb new file mode 100644 index 00000000000..6918c1cab77 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/client.rb @@ -0,0 +1,26 @@ +# GANLIA monitoring client recipe + +package "ganglia-monitor" + +# Begin recipe transactions +Chef::Log.debug("BEGIN ganlia-client") + +admin_interface = Ganglia::Evaluator.get_value_by_type(node,:interface_eval) + +template "/etc/ganglia/gmond.conf" do + source "gmond.conf.erb" + variables( :admin_interface => admin_interface ) + notifies :restart, "service[ganglia-monitor]" +end + +service "ganglia-monitor" do + supports :restart => true + pattern "gmond" + running true + enabled true + action [ :enable, :start ] +end + +# End of recipe transactions +Chef::Log.debug("END ganglia-client") + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/default.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/default.rb new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/default.rb @@ -0,0 +1 @@ + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/server.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/server.rb new file mode 100644 index 00000000000..721892d9e90 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/recipes/server.rb @@ -0,0 +1,31 @@ +# GANLIA monitoring server recipe + +include_recipe "ganglia::client" + +package "gmetad" +package "ganglia-webfrontend" + +# Begin recipe transactions +Chef::Log.debug("BEGIN ganglia-server") + +link "/etc/apache2/conf.d/ganglia.conf" do + to "/etc/ganglia-webfrontend/apache.conf" + not_if "test -L /etc/apache2/conf.d/ganglia.conf" + notifies :reload, "service[apache2]" +end + +template "/etc/ganglia/gmetad.conf" do + source "gmetad.conf.erb" + notifies :restart, "service[gmetad]" +end + +service "gmetad" do + supports :restart => true + running true + enabled true + action [ :enable, :start ] +end + +# End of recipe transactions +Chef::Log.debug("END ganglia-server") + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/templates/default/gmetad.conf.erb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/templates/default/gmetad.conf.erb new file mode 100644 index 00000000000..9bfef9d6bc5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/templates/default/gmetad.conf.erb @@ -0,0 +1,125 @@ +# This is an example of a Ganglia Meta Daemon configuration file +# http://ganglia.sourceforge.net/ +# +# $Id: gmetad.conf.in 2014 2009-08-10 10:44:09Z d_pocock $ +# +#------------------------------------------------------------------------------- +# Setting the debug_level to 1 will keep daemon in the forground and +# show only error messages. Setting this value higher than 1 will make +# gmetad output debugging information and stay in the foreground. +# default: 0 +# debug_level 10 +# +#------------------------------------------------------------------------------- +# What to monitor. The most important section of this file. +# +# The data_source tag specifies either a cluster or a grid to +# monitor. If we detect the source is a cluster, we will maintain a complete +# set of RRD databases for it, which can be used to create historical +# graphs of the metrics. If the source is a grid (it comes from another gmetad), +# we will only maintain summary RRDs for it. +# +# Format: +# data_source "my cluster" [polling interval] address1:port addreses2:port ... +# +# The keyword 'data_source' must immediately be followed by a unique +# string which identifies the source, then an optional polling interval in +# seconds. The source will be polled at this interval on average. +# If the polling interval is omitted, 15sec is asssumed. +# +# A list of machines which service the data source follows, in the +# format ip:port, or name:port. If a port is not specified then 8649 +# (the default gmond port) is assumed. +# default: There is no default value +# +# data_source "my cluster" 10 localhost my.machine.edu:8649 1.2.3.5:8655 +# data_source "my grid" 50 1.3.4.7:8655 grid.org:8651 grid-backup.org:8651 +# data_source "another source" 1.3.4.7:8655 1.3.4.8 + +data_source "Crowbar PoC" localhost + +# +# Round-Robin Archives +# You can specify custom Round-Robin archives here (defaults are listed below) +# +# RRAs "RRA:AVERAGE:0.5:1:244" "RRA:AVERAGE:0.5:24:244" "RRA:AVERAGE:0.5:168:244" "RRA:AVERAGE:0.5:672:244" \ +# "RRA:AVERAGE:0.5:5760:374" +# + +# +#------------------------------------------------------------------------------- +# Scalability mode. If on, we summarize over downstream grids, and respect +# authority tags. If off, we take on 2.5.0-era behavior: we do not wrap our output +# in tags, we ignore all tags we see, and always assume +# we are the "authority" on data source feeds. This approach does not scale to +# large groups of clusters, but is provided for backwards compatibility. +# default: on +# scalable off +# +#------------------------------------------------------------------------------- +# The name of this Grid. All the data sources above will be wrapped in a GRID +# tag with this name. +# default: unspecified +# gridname "MyGrid" +# +#------------------------------------------------------------------------------- +# The authority URL for this grid. Used by other gmetads to locate graphs +# for our data sources. Generally points to a ganglia/ +# website on this machine. +# default: "http://hostname/ganglia/", +# where hostname is the name of this machine, as defined by gethostname(). +# authority "http://mycluster.org/newprefix/" +# +#------------------------------------------------------------------------------- +# List of machines this gmetad will share XML with. Localhost +# is always trusted. +# default: There is no default value +# trusted_hosts 127.0.0.1 169.229.50.165 my.gmetad.org +# +#------------------------------------------------------------------------------- +# If you want any host which connects to the gmetad XML to receive +# data, then set this value to "on" +# default: off +# all_trusted on +# +#------------------------------------------------------------------------------- +# If you don't want gmetad to setuid then set this to off +# default: on +# setuid off +# +#------------------------------------------------------------------------------- +# User gmetad will setuid to (defaults to "nobody") +# default: "nobody" +# setuid_username "nobody" +# +#------------------------------------------------------------------------------- +# The port gmetad will answer requests for XML +# default: 8651 +# xml_port 8651 +# +#------------------------------------------------------------------------------- +# The port gmetad will answer queries for XML. This facility allows +# simple subtree and summation views of the XML tree. +# default: 8652 +# interactive_port 8652 +# +#------------------------------------------------------------------------------- +# The number of threads answering XML requests +# default: 4 +# server_threads 10 +# +#------------------------------------------------------------------------------- +# Where gmetad stores its round-robin databases +# default: "/var/lib/ganglia/rrds" +# rrd_rootdir "/some/other/place" +# +#------------------------------------------------------------------------------- +# In earlier versions of gmetad, hostnames were handled in a case +# sensitive manner +# If your hostname directories have been renamed to lower case, +# set this option to 0 to disable backward compatibility. +# From version 3.2, backwards compatibility will be disabled by default. +# default: 1 (for gmetad < 3.2) +# default: 0 (for gmetad >= 3.2) +case_sensitive_hostnames 1 + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/templates/default/gmond.conf.erb b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/templates/default/gmond.conf.erb new file mode 100644 index 00000000000..00577f66e36 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/cookbooks/ganglia/templates/default/gmond.conf.erb @@ -0,0 +1,339 @@ +/* This configuration is as close to 2.5.x default behavior as possible + The values closely match ./gmond/metric.h definitions in 2.5.x */ +globals { + daemonize = yes + setuid = yes + user = ganglia + debug_level = 0 + max_udp_msg_len = 1472 + mute = no + deaf = no + host_dmax = 1200 /*secs */ + cleanup_threshold = 300 /*secs */ + gexec = no + send_metadata_interval = 0 +} + +/* If a cluster attribute is specified, then all gmond hosts are wrapped inside + * of a tag. If you do not specify a cluster tag, then all will + * NOT be wrapped inside of a tag. */ +cluster { + name = "Crowbar PoC" + owner = "unspecified" + latlong = "unspecified" + url = "unspecified" +} + +/* The host section describes attributes of the host, like the location */ +host { + location = "unspecified" +} + +/* Feel free to specify as many udp_send_channels as you like. Gmond + used to only support having a single channel */ +udp_send_channel { + mcast_join = 239.2.11.71 + mcast_if = <%= @admin_interface %> + port = 8649 + ttl = 1 +} + +/* You can specify as many udp_recv_channels as you like as well. */ +udp_recv_channel { + mcast_join = 239.2.11.71 + mcast_if = <%= @admin_interface %> + port = 8649 + bind = 239.2.11.71 +} + +/* You can specify as many tcp_accept_channels as you like to share + an xml description of the state of the cluster */ +tcp_accept_channel { + port = 8649 +} + +/* Each metrics module that is referenced by gmond must be specified and + loaded. If the module has been statically linked with gmond, it does not + require a load path. However all dynamically loadable modules must include + a load path. */ +modules { + module { + name = "core_metrics" + } + module { + name = "cpu_module" + path = "/usr/lib/ganglia/modcpu.so" + } + module { + name = "disk_module" + path = "/usr/lib/ganglia/moddisk.so" + } + module { + name = "load_module" + path = "/usr/lib/ganglia/modload.so" + } + module { + name = "mem_module" + path = "/usr/lib/ganglia/modmem.so" + } + module { + name = "net_module" + path = "/usr/lib/ganglia/modnet.so" + } + module { + name = "proc_module" + path = "/usr/lib/ganglia/modproc.so" + } + module { + name = "sys_module" + path = "/usr/lib/ganglia/modsys.so" + } +} + +include ('/etc/ganglia/conf.d/*.conf') + + +/* The old internal 2.5.x metric array has been replaced by the following + collection_group directives. What follows is the default behavior for + collecting and sending metrics that is as close to 2.5.x behavior as + possible. */ + +/* This collection group will cause a heartbeat (or beacon) to be sent every + 20 seconds. In the heartbeat is the GMOND_STARTED data which expresses + the age of the running gmond. */ +collection_group { + collect_once = yes + time_threshold = 20 + metric { + name = "heartbeat" + } +} + +/* This collection group will send general info about this host every 1200 secs. + This information doesn't change between reboots and is only collected once. */ +collection_group { + collect_once = yes + time_threshold = 1200 + metric { + name = "cpu_num" + title = "CPU Count" + } + metric { + name = "cpu_speed" + title = "CPU Speed" + } + metric { + name = "mem_total" + title = "Memory Total" + } + /* Should this be here? Swap can be added/removed between reboots. */ + metric { + name = "swap_total" + title = "Swap Space Total" + } + metric { + name = "boottime" + title = "Last Boot Time" + } + metric { + name = "machine_type" + title = "Machine Type" + } + metric { + name = "os_name" + title = "Operating System" + } + metric { + name = "os_release" + title = "Operating System Release" + } + metric { + name = "location" + title = "Location" + } +} + +/* This collection group will send the status of gexecd for this host every 300 secs */ +/* Unlike 2.5.x the default behavior is to report gexecd OFF. */ +collection_group { + collect_once = yes + time_threshold = 300 + metric { + name = "gexec" + title = "Gexec Status" + } +} + +/* This collection group will collect the CPU status info every 20 secs. + The time threshold is set to 90 seconds. In honesty, this time_threshold could be + set significantly higher to reduce unneccessary network chatter. */ +collection_group { + collect_every = 20 + time_threshold = 90 + /* CPU status */ + metric { + name = "cpu_user" + value_threshold = "1.0" + title = "CPU User" + } + metric { + name = "cpu_system" + value_threshold = "1.0" + title = "CPU System" + } + metric { + name = "cpu_idle" + value_threshold = "5.0" + title = "CPU Idle" + } + metric { + name = "cpu_nice" + value_threshold = "1.0" + title = "CPU Nice" + } + metric { + name = "cpu_aidle" + value_threshold = "5.0" + title = "CPU aidle" + } + metric { + name = "cpu_wio" + value_threshold = "1.0" + title = "CPU wio" + } + /* The next two metrics are optional if you want more detail... + ... since they are accounted for in cpu_system. + metric { + name = "cpu_intr" + value_threshold = "1.0" + title = "CPU intr" + } + metric { + name = "cpu_sintr" + value_threshold = "1.0" + title = "CPU sintr" + } + */ +} + +collection_group { + collect_every = 20 + time_threshold = 90 + /* Load Averages */ + metric { + name = "load_one" + value_threshold = "1.0" + title = "One Minute Load Average" + } + metric { + name = "load_five" + value_threshold = "1.0" + title = "Five Minute Load Average" + } + metric { + name = "load_fifteen" + value_threshold = "1.0" + title = "Fifteen Minute Load Average" + } +} + +/* This group collects the number of running and total processes */ +collection_group { + collect_every = 80 + time_threshold = 950 + metric { + name = "proc_run" + value_threshold = "1.0" + title = "Total Running Processes" + } + metric { + name = "proc_total" + value_threshold = "1.0" + title = "Total Processes" + } +} + +/* This collection group grabs the volatile memory metrics every 40 secs and + sends them at least every 180 secs. This time_threshold can be increased + significantly to reduce unneeded network traffic. */ +collection_group { + collect_every = 40 + time_threshold = 180 + metric { + name = "mem_free" + value_threshold = "1024.0" + title = "Free Memory" + } + metric { + name = "mem_shared" + value_threshold = "1024.0" + title = "Shared Memory" + } + metric { + name = "mem_buffers" + value_threshold = "1024.0" + title = "Memory Buffers" + } + metric { + name = "mem_cached" + value_threshold = "1024.0" + title = "Cached Memory" + } + metric { + name = "swap_free" + value_threshold = "1024.0" + title = "Free Swap Space" + } +} + +collection_group { + collect_every = 40 + time_threshold = 300 + metric { + name = "bytes_out" + value_threshold = 4096 + title = "Bytes Sent" + } + metric { + name = "bytes_in" + value_threshold = 4096 + title = "Bytes Received" + } + metric { + name = "pkts_in" + value_threshold = 256 + title = "Packets Received" + } + metric { + name = "pkts_out" + value_threshold = 256 + title = "Packets Sent" + } +} + +/* Different than 2.5.x default since the old config made no sense */ +collection_group { + collect_every = 1800 + time_threshold = 3600 + metric { + name = "disk_total" + value_threshold = 1.0 + title = "Total Disk Space" + } +} + +collection_group { + collect_every = 40 + time_threshold = 180 + metric { + name = "disk_free" + value_threshold = 1.0 + title = "Disk Space Available" + } + metric { + name = "part_max_used" + value_threshold = 1.0 + title = "Maximum Disk Space Used" + } +} + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/data_bags/crowbar/bc-template-ganglia.json b/crowbar/change-image/dell/barclamps/ganglia/chef/data_bags/crowbar/bc-template-ganglia.json new file mode 100644 index 00000000000..a5097f87d58 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/data_bags/crowbar/bc-template-ganglia.json @@ -0,0 +1,27 @@ +{ + "id": "bc-template-ganglia", + "description": "a common Ganglia service for the cluster that can be used by other barclamps", + "attributes": { + "ganglia": { + "interface_eval": "Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, \"admin\").interface" + } + }, + "deployment": { + "ganglia": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "ganglia-server", "ganglia-client" ] + ], + "config": { + "environment": "ganglia-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/data_bags/crowbar/bc-template-ganglia.schema b/crowbar/change-image/dell/barclamps/ganglia/chef/data_bags/crowbar/bc-template-ganglia.schema new file mode 100644 index 00000000000..02e76710630 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/data_bags/crowbar/bc-template-ganglia.schema @@ -0,0 +1,69 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-ganglia-|^bc-template-ganglia$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "ganglia": { + "type": "map", + "required": true, + "mapping": { + "interface_eval": { "type": "str", "required": true } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "ganglia": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/roles/ganglia-client.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/roles/ganglia-client.rb new file mode 100644 index 00000000000..1d003382076 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/roles/ganglia-client.rb @@ -0,0 +1,8 @@ + +name "ganglia-client" +description "GANGLIA Client Role - Nodes in the environment that should be monitored" +run_list( + "recipe[ganglia::client]" +) +default_attributes() +override_attributes() diff --git a/crowbar/change-image/dell/barclamps/ganglia/chef/roles/ganglia-server.rb b/crowbar/change-image/dell/barclamps/ganglia/chef/roles/ganglia-server.rb new file mode 100644 index 00000000000..cc603b450ed --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/chef/roles/ganglia-server.rb @@ -0,0 +1,9 @@ + +name "ganglia-server" +description "GANGLIA Server Role - GANGLIA master for the cloud" +run_list( + "recipe[ganglia::server]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/ganglia/command_line/crowbar_ganglia b/crowbar/change-image/dell/barclamps/ganglia/command_line/crowbar_ganglia new file mode 100755 index 00000000000..e14481daa4b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/command_line/crowbar_ganglia @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "ganglia" + +main diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/ganglia/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/compat b/crowbar/change-image/dell/barclamps/ganglia/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/control b/crowbar/change-image/dell/barclamps/ganglia/debian/control new file mode 100644 index 00000000000..232c4768cee --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-ganglia +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-ganglia +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the ganglia barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/copyright b/crowbar/change-image/dell/barclamps/ganglia/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/postinst b/crowbar/change-image/dell/barclamps/ganglia/debian/postinst new file mode 100644 index 00000000000..20724309131 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-ganglia/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-ganglia/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-ganglia/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/rules b/crowbar/change-image/dell/barclamps/ganglia/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/ganglia/debian/source/format b/crowbar/change-image/dell/barclamps/ganglia/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/ganglia/version.sh b/crowbar/change-image/dell/barclamps/ganglia/version.sh new file mode 100644 index 00000000000..3224c1bc199 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ganglia/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=ganglia +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/ipmi/app/controllers/ipmi_controller.rb b/crowbar/change-image/dell/barclamps/ipmi/app/controllers/ipmi_controller.rb new file mode 100755 index 00000000000..d3a629adf19 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/app/controllers/ipmi_controller.rb @@ -0,0 +1,20 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class IpmiController < BarclampController + def initialize + @service_object = IpmiService.new logger + end +end diff --git a/crowbar/change-image/dell/barclamps/ipmi/app/models/ipmi_service.rb b/crowbar/change-image/dell/barclamps/ipmi/app/models/ipmi_service.rb new file mode 100755 index 00000000000..5cae1ee2938 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/app/models/ipmi_service.rb @@ -0,0 +1,51 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class IpmiService < ServiceObject + + def initialize(thelogger) + @bc_name = "ipmi" + @logger = thelogger + end + + def create_proposal + @logger.debug("IPMI create_proposal: entering") + base = super + @logger.debug("IPMI create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("IPMI transition: make sure that network role is on all nodes: #{name} for #{state}") + + # + # If we are discovering the node, make sure that we add the ipmi role to the node + # + if state == "discovered" + @logger.debug("IPMI transition: installed state for #{name} for #{state}") + db = ProposalObject.find_proposal "ipmi", inst + role = RoleObject.find_role_by_name "ipmi-config-#{inst}" + result = add_role_to_instance_and_node("ipmi", inst, name, db, role, "ipmi-configure") + @logger.debug("ipmi transition: leaving from installed state for #{name} for #{state}") + a = [200, {}] if result + a = [400, "Failed to add role to node"] unless result + return a + end + + @logger.debug("ipmi transition: leaving for #{name} for #{state}") + [200, {}] + end + +end diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/attributes/default.rb b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/attributes/default.rb new file mode 100755 index 00000000000..4685a420d3e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/attributes/default.rb @@ -0,0 +1,28 @@ +# +# Copyright (c) 2011 Dell Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default[:ipmi][:config] = {} +default[:ipmi][:config][:environment] = "bios-config-default" + +default[:ipmi][:bmc_user] = "crowbar" +default[:ipmi][:bmc_password] = "crowbar" +default[:ipmi][:bmc_enable] = true +default[:ipmi][:debug] = true + +default[:crowbar][:network][:bmc][:address] = "192.168.124.161" +default[:crowbar][:network][:bmc][:netmask] = "255.255.255.0" +default[:crowbar][:network][:bmc][:router] = "192.168.124.10" + diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/metadata.json b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/metadata.json new file mode 100755 index 00000000000..3a09289587f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/metadata.json @@ -0,0 +1,41 @@ +{ + "platforms": { + + }, + "suggestions": { + + }, + "license": "Apache 2.0", + "conflicting": { + + }, + "long_description": "", + "providing": { + + }, + "description": "configures BMC", + "version": "0.1.0", + "maintainer": "Dell, Inc.", + "replacing": { + + }, + "attributes": { + + }, + "maintainer_email": "openstack@dell.com", + "name": "ipmi", + "recipes": { + + }, + "groupings": { + + }, + "dependencies": { + "utils": [ + + ] + }, + "recommendations": { + + } +} \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/metadata.rb b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/metadata.rb new file mode 100755 index 00000000000..a08651e87fa --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/metadata.rb @@ -0,0 +1,24 @@ +# +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: andi abes +# +maintainer "Dell, Inc." +maintainer_email "crowbar@dell.com" +license "Apache 2.0" +description "Configures IPMI/BMC parameters for management access" +long_description "Configures IPMI/BMC parameters for management access" +version "0.1" +depends "utils" \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/recipes/ipmi-configure.rb b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/recipes/ipmi-configure.rb new file mode 100755 index 00000000000..90a999c6967 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/cookbooks/ipmi/recipes/ipmi-configure.rb @@ -0,0 +1,117 @@ +# +# Copyright (c) 2011 Dell Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Note : This script runs on both the admin and compute nodes. +# It intentionally ignores the bios->enable node data flag. + +include_recipe "utils" + + + +# for now, assume it's there. +# package "ipmitool" + + +bmc_user = node[:ipmi][:bmc_user] +bmc_password = node[:ipmi][:bmc_password] +bmc_address = node[:crowbar][:network][:bmc][:address] +bmc_netmask = node[:crowbar][:network][:bmc][:netmask] +bmc_router = node[:crowbar][:network][:bmc][:router] + +def log_debug(msg) + log(msg) {level :info} if node[:ipmi][:debug] +end + +## utility to check if a lan parameter needs to be set (if it's current value is different than desired one). +def check_ipmi_lan_value(header, desired) + c_awk = "awk -F : ' /#{header}\\W*:/ { print \$2 } '" + current = %x{ipmitool lan print 1 | #{c_awk} } + current = current.chomp.strip + log_debug " header #{header} should have: #{desired} current: #{current}" + ret = current.casecmp(desired) == 0 # true if matching + log_debug ("#{ret ? "will not": "will" } change value" ) + ret +end + + + +if node[:ipmi][:bmc_enable] + # Make sure the IPMI kernel modules are installed + bash "install-ipmi_si" do + code "/sbin/modprobe ipmi_si" + not_if { ::File.exists?("/sys/module/ipmi_si") } + returns [0,1] + ignore_failure true + end + + bash "install-devintf" do + code "/sbin/modprobe ipmi_devintf" + not_if { ::File.exists?("/sys/module/ipmi_devintf") } + returns [0,1] + ignore_failure true + end + + ## failed to load ipmi support... occurs on virtual machines and machines without IPMI + if !File.exists?("/sys/module/ipmi_si") + node[:ipmi][:bmc_enable] = false # won't try again.. + log ("Unsupported product found #{node[:dmi][:system][:product_name]} - skipping IPMI") {level :warn} + else + ### lan parameters to check and set. The loop that follows iterates over this array. + # [0] = name in "print" output, [1] command to issue, [2] desired value. + lan_params = [ + [ "IP Address" ,"ipmitool lan set 1 ipaddr #{bmc_address}", bmc_address ] , + [ "IP Address Source" ,"ipmitool lan set 1 ipsrc static", "Static Address" ] , + [ "Subnet Mask" , "ipmitool lan set 1 netmask #{bmc_netmask}", bmc_netmask ] + ] + + lan_params << [ "Default Gateway IP", "ipmitool lan set 1 defgw ipaddr #{bmc_router}", bmc_router ] unless bmc_router.nil? + + + # Set BMC LAN parameters + lan_params.each { |param| + bash "bmc-set-lan-#{param[0]}" do + code <<-EOH +#{param[1]} +sleep 1 +EOH + end unless check_ipmi_lan_value(param[0], param[2]) + } + + # Set the BMC channel user parameters + bash "bmc-set-user" do + code <<-EOH + ipmitool user set name 3 #{bmc_user} + sleep 1 + ipmitool user set password 3 #{bmc_password} + sleep 1 + ipmitool user priv 3 4 1 + sleep 1 + ipmitool channel setaccess 1 3 callin=on link=on ipmi=on privilege=4 + sleep 1 + ipmitool user enable 3 + sleep 1 +EOH + end + + s = "" + s << "BMC info: user: [#{bmc_user}] " + s << "password [#{bmc_password}] " + s << "address:[#{bmc_address}] " + s << "netmask [#{bmc_netmask}]" + s << "router [#{bmc_router}]" unless bmc_router.nil? + log (s) { level :info } if node[:ipmi][:debug] + + end +end diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/data_bags/crowbar/bc-template-ipmi.json b/crowbar/change-image/dell/barclamps/ipmi/chef/data_bags/crowbar/bc-template-ipmi.json new file mode 100755 index 00000000000..a1405f07474 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/data_bags/crowbar/bc-template-ipmi.json @@ -0,0 +1,30 @@ +{ + "id": "bc-template-ipmi", + "description": "The default proposal for the ipmi barclamp", + "attributes": { + "ipmi": { + "bmc_enable" : true, + "bmc_user": "crowbar", + "bmc_password": "crowbar", + "debug": true + } + }, + "deployment": { + "ipmi": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "ipmi-configure" ] + ], + "config": { + "environment": "ipmi-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/data_bags/crowbar/bc-template-ipmi.schema b/crowbar/change-image/dell/barclamps/ipmi/chef/data_bags/crowbar/bc-template-ipmi.schema new file mode 100755 index 00000000000..4042749d557 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/data_bags/crowbar/bc-template-ipmi.schema @@ -0,0 +1,45 @@ +{ + "type": "map", "required": true, "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-ipmi-|^bc-template-ipmi$/" }, + "description": { "type": "str", "required": true }, + "attributes": { "type": "map", "required": true, "mapping": { + "ipmi": { "type": "map", "required": true, "mapping": { + "bmc_enable": { "type": "bool", "required": true}, + "bmc_user": { "type": "str", "required": true }, + "bmc_password": { "type": "str", "required": true }, + "debug": { "type": "bool", "required": true} + } + } + } + }, + "deployment": { "type": "map", "required": true, "mapping": { + "ipmi": { "type": "map", "required": true, "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { "type": "map", "required": true, "mapping": { + = : { "type": "seq", "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { "type": "seq", "required": true, + "sequence": [ { "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { "type": "map", "required": true, "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { "type": "seq", "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/ipmi/chef/roles/ipmi-configure.rb b/crowbar/change-image/dell/barclamps/ipmi/chef/roles/ipmi-configure.rb new file mode 100755 index 00000000000..7a30e535712 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ipmi/chef/roles/ipmi-configure.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name: bios +# Role: bios-install +# +# Copyright (c) 2011 Dell Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name "ipmi-configure" +description "IPMI configure - configure the BMC to allow remote management" +run_list( + "recipe[ipmi::ipmi-configure]" +) diff --git a/crowbar/change-image/dell/barclamps/logging/Makefile b/crowbar/change-image/dell/barclamps/logging/Makefile new file mode 100644 index 00000000000..598bd8be13f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-logging" + +distclean: + @echo "Dist-Cleaning barclamp-logging" + +all: clean build install + +build: + @echo "Building barclamp-logging" + +install: + @echo "Installing barclamp-logging" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-logging + cp -r chef ${DESTDIR}/usr/share/barclamp-logging + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/logging/app/controllers/logging_controller.rb b/crowbar/change-image/dell/barclamps/logging/app/controllers/logging_controller.rb new file mode 100644 index 00000000000..4db254eafc4 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/app/controllers/logging_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class LoggingController < BarclampController + def initialize + @service_object = LoggingService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/logging/app/models/logging_service.rb b/crowbar/change-image/dell/barclamps/logging/app/models/logging_service.rb new file mode 100644 index 00000000000..c55e0ade681 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/app/models/logging_service.rb @@ -0,0 +1,64 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class LoggingService < ServiceObject + + def initialize(thelogger) + @bc_name = "logging" + @logger = thelogger + end + + def create_proposal + @logger.debug("Logging create_proposal: entering") + base = super + @logger.debug("Logging create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("Logging transition: entering: #{name} for #{state}") + + # + # If we are discovering the node, make sure that we add the logging client or server to the node + # + if state == "discovered" + @logger.debug("Logging transition: discovered state for #{name} for #{state}") + db = ProposalObject.find_proposal "logging", inst + role = RoleObject.find_role_by_name "logging-config-#{inst}" + + if role.override_attributes["logging"]["elements"]["logging-server"].nil? or + role.override_attributes["logging"]["elements"]["logging-server"].empty? + @logger.debug("Logging transition: make sure that logging-server role is on first: #{name} for #{state}") + result = add_role_to_instance_and_node("logging", inst, name, db, role, "logging-server") + else + node = NodeObject.find_node_by_name name + unless node.role? "logging-server" + @logger.debug("Logging transition: make sure that logging-client role is on all nodes but first: #{name} for #{state}") + result = add_role_to_instance_and_node("logging", inst, name, db, role, "logging-client") + end + end + + @logger.debug("Logging transition: leaving from discovered state for #{name} for #{state}") + a = [200, {}] if result + a = [400, "Failed to add logging role to node"] unless result + return a + end + + @logger.debug("Logging transition: leaving for #{name} for #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + +end + diff --git a/crowbar/change-image/dell/barclamps/logging/barclamp-logging.spec b/crowbar/change-image/dell/barclamps/logging/barclamp-logging.spec new file mode 100644 index 00000000000..0ebeafbebe5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/barclamp-logging.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-logging +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: centralized logging system based on syslog +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages logging deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-logging/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-logging/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-logging/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/logging/build_deb.sh b/crowbar/change-image/dell/barclamps/logging/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/logging/build_rpm.sh b/crowbar/change-image/dell/barclamps/logging/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/metadata.json b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/metadata.json new file mode 100644 index 00000000000..3ed5739812b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/metadata.json @@ -0,0 +1,37 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "logging-server": "Installs the logging server", + "logging-client": "Installs the logging client" + }, + "maintainer_email": "crowbar@dell.com", + "name": "logging", + "conflicting": { + }, + "description": "Installs the logging", + "long_description": "" +} diff --git a/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/client.rb b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/client.rb new file mode 100644 index 00000000000..ffb1a96df60 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/client.rb @@ -0,0 +1,28 @@ + +package "rsyslog" + +env_filter = " AND environment:#{node[:logging][:config][:environment]}" +servers = search(:node, "roles:logging\\-server#{env_filter}") + +if servers.nil? + servers = [] +else + servers = servers.map { |x| Chef::Recipe::Barclamp::Inventory.get_network_by_type(x, "admin").address } +end + +service "rsyslog" do + supports :restart => true, :status => true, :reload => true + running true + enabled true + action [ :enable, :start ] +end + +template "/etc/rsyslog.d/10-crowbar-client.conf" do + owner "root" + group "root" + mode 0644 + source "rsyslog.client.erb" + variables(:servers => servers) + notifies :restart, "service[rsyslog]" +end + diff --git a/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/default.rb b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/default.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/server.rb b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/server.rb new file mode 100644 index 00000000000..1fc6d553830 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/recipes/server.rb @@ -0,0 +1,19 @@ + + +package "rsyslog" + +service "rsyslog" do + supports :restart => true, :status => true, :reload => true + running true + enabled true + action [ :enable, :start ] +end + +template "/etc/rsyslog.d/10-crowbar-server.conf" do + owner "root" + group "root" + mode 0644 + source "rsyslog.server.erb" + notifies :restart, "service[rsyslog]" +end + diff --git a/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/templates/default/rsyslog.client.erb b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/templates/default/rsyslog.client.erb new file mode 100644 index 00000000000..d2ed2b13113 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/templates/default/rsyslog.client.erb @@ -0,0 +1,5 @@ + +<% @servers.each do |server| -%> +*.* @@<%= server %> +<% end -%> + diff --git a/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/templates/default/rsyslog.server.erb b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/templates/default/rsyslog.server.erb new file mode 100644 index 00000000000..aef9510a72f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/cookbooks/logging/templates/default/rsyslog.server.erb @@ -0,0 +1,8 @@ + +# remote rsyslog from Ubuntu will log here using TCP +$ModLoad imtcp +$InputTCPServerRun 514 + +# remote syslog from CentOS will log here using UDP +$ModLoad imudp +$InputUDPServerRun 514 diff --git a/crowbar/change-image/dell/barclamps/logging/chef/data_bags/crowbar/bc-template-logging.json b/crowbar/change-image/dell/barclamps/logging/chef/data_bags/crowbar/bc-template-logging.json new file mode 100644 index 00000000000..983498cc71a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/data_bags/crowbar/bc-template-logging.json @@ -0,0 +1,27 @@ +{ + "id": "bc-template-logging", + "description": "centralized logging system based on syslog", + "attributes": { + "logging": { + "external_servers": [ ] + } + }, + "deployment": { + "logging": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "logging-server", "logging-client" ] + ], + "config": { + "environment": "logging-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/logging/chef/data_bags/crowbar/bc-template-logging.schema b/crowbar/change-image/dell/barclamps/logging/chef/data_bags/crowbar/bc-template-logging.schema new file mode 100644 index 00000000000..717adcd0c1f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/data_bags/crowbar/bc-template-logging.schema @@ -0,0 +1,73 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-logging-|^bc-template-logging$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "logging": { + "type": "map", + "required": true, + "mapping": { + "external_servers": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "logging": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/logging/chef/roles/logging-client.rb b/crowbar/change-image/dell/barclamps/logging/chef/roles/logging-client.rb new file mode 100644 index 00000000000..4278a418954 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/roles/logging-client.rb @@ -0,0 +1,9 @@ + +name "logging-client" +description "Logging Client Role - Logging client for the cloud points to Master" +run_list( + "recipe[logging::client]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/logging/chef/roles/logging-server.rb b/crowbar/change-image/dell/barclamps/logging/chef/roles/logging-server.rb new file mode 100644 index 00000000000..097bcbb2562 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/chef/roles/logging-server.rb @@ -0,0 +1,9 @@ + +name "logging-server" +description "Logging Servier Role - Logging master for the cloud" +run_list( + "recipe[logging::server]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/logging/command_line/crowbar_logging b/crowbar/change-image/dell/barclamps/logging/command_line/crowbar_logging new file mode 100755 index 00000000000..1d16ca55027 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/command_line/crowbar_logging @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "logging" + +main diff --git a/crowbar/change-image/dell/barclamps/logging/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/logging/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/logging/debian/compat b/crowbar/change-image/dell/barclamps/logging/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/logging/debian/control b/crowbar/change-image/dell/barclamps/logging/debian/control new file mode 100644 index 00000000000..1e43c1291ef --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-logging +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-logging +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the logging barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/logging/debian/copyright b/crowbar/change-image/dell/barclamps/logging/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/logging/debian/postinst b/crowbar/change-image/dell/barclamps/logging/debian/postinst new file mode 100644 index 00000000000..14e5692f406 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-logging/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-logging/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-logging/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/logging/debian/rules b/crowbar/change-image/dell/barclamps/logging/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/logging/debian/source/format b/crowbar/change-image/dell/barclamps/logging/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/logging/version.sh b/crowbar/change-image/dell/barclamps/logging/version.sh new file mode 100644 index 00000000000..d989dc95988 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/logging/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=logging +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/mk_pkg.sh b/crowbar/change-image/dell/barclamps/mk_pkg.sh new file mode 100755 index 00000000000..0edc75aee9c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/mk_pkg.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# copies the ganglia packing scripts into the specified directory making the changes for the name. + +NEW_PKG=$1 + +cd ganglia +FILE_LIST=`find debian version.sh Makefile build_* barclamp* -type f | grep -v svn` +cd .. + +cd $1 +mkdir -p debian/source + +svn delete debian/changelog +svn delete debian/files + +for i in $FILE_LIST +do + file=`echo $i | sed -e "s/ganglia/$1/"` + echo $file + + cp ../ganglia/$i $file + + sed -i -e "s/ganglia/$1/g" $file +done + +svn add version.sh +svn add debian/changelog.tmpl + +cd - diff --git a/crowbar/change-image/dell/barclamps/nagios/Makefile b/crowbar/change-image/dell/barclamps/nagios/Makefile new file mode 100644 index 00000000000..82820d3ea99 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-nagios" + +distclean: + @echo "Dist-Cleaning barclamp-nagios" + +all: clean build install + +build: + @echo "Building barclamp-nagios" + +install: + @echo "Installing barclamp-nagios" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-nagios + cp -r chef ${DESTDIR}/usr/share/barclamp-nagios + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/nagios/app/controllers/nagios_controller.rb b/crowbar/change-image/dell/barclamps/nagios/app/controllers/nagios_controller.rb new file mode 100644 index 00000000000..4824aff3d3e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/app/controllers/nagios_controller.rb @@ -0,0 +1,7 @@ + +class NagiosController < BarclampController + def initialize + @service_object = NagiosService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/nagios/app/models/nagios_service.rb b/crowbar/change-image/dell/barclamps/nagios/app/models/nagios_service.rb new file mode 100644 index 00000000000..0d6abac23d0 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/app/models/nagios_service.rb @@ -0,0 +1,78 @@ + +class NagiosService < ServiceObject + + def initialize(thelogger) + @bc_name = "nagios" + @logger = thelogger + end + + def create_proposal + @logger.debug("Nagios create_proposal: entering") + base = super + @logger.debug("Nagios create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("Nagios transition: make sure that network role is on all nodes: #{name} for #{state}") + + # + # If we are discovering the node, make sure that we add the nagios client or server to the node + # + if state == "discovered" + @logger.debug("Nagios transition: discovered state for #{name} for #{state}") + db = ProposalObject.find_proposal "nagios", inst + role = RoleObject.find_role_by_name "nagios-config-#{inst}" + + if role.override_attributes["nagios"]["elements"]["nagios-server"].nil? or + role.override_attributes["nagios"]["elements"]["nagios-server"].empty? + @logger.debug("Nagios transition: make sure that nagios-server role is on first: #{name} for #{state}") + result = add_role_to_instance_and_node("nagios", inst, name, db, role, "nagios-server") + else + node = NodeObject.find_node_by_name name + unless node.role? "nagios-server" + @logger.debug("Nagios transition: make sure that nagios-client role is on all nodes but first: #{name} for #{state}") + result = add_role_to_instance_and_node("nagios", inst, name, db, role, "nagios-client") + else + result = true + end + end + + # Set up the client url + if result + role = RoleObject.find_role_by_name "nagios-config-#{inst}" + + # Get the server IP address + server_ip = nil + [ "nagios-server" ].each do |element| + tnodes = role.override_attributes["nagios"]["elements"][element] + next if tnodes.nil? or tnodes.empty? + tnodes.each do |n| + next if n.nil? + node = NodeObject.find_node_by_name(n) + server_ip = node.get_network_by_type("admin")["address"] + end + end + + unless server_ip.nil? + node = NodeObject.find_node_by_name(name) + node["crowbar"] = {} if node["crowbar"].nil? + node["crowbar"]["links"] = {} if node["crowbar"]["links"].nil? + node["crowbar"]["links"]["Nagios"] = "http://#{server_ip}/nagios3/cgi-bin/extinfo.cgi?type=1&host=#{node.shortname}" + node.save + end + end + + @logger.debug("Nagios transition: leaving from discovered state for #{name} for #{state}") + a = [200, NodeObject.find_node_by_name(name).to_hash ] if result + a = [400, "Failed to add role to node"] unless result + return a + end + + @logger.debug("Nagios transition: leaving for #{name} for #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + +end + + diff --git a/crowbar/change-image/dell/barclamps/nagios/barclamp-nagios.spec b/crowbar/change-image/dell/barclamps/nagios/barclamp-nagios.spec new file mode 100644 index 00000000000..c603921c36f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/barclamp-nagios.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-nagios +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: common Nagios service for the cluster that can be used by other barclamps +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages nagios deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-nagios/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-nagios/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-nagios/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/nagios/build_deb.sh b/crowbar/change-image/dell/barclamps/nagios/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/nagios/build_rpm.sh b/crowbar/change-image/dell/barclamps/nagios/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/README.rdoc b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/README.rdoc new file mode 100644 index 00000000000..614abc7d40c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/README.rdoc @@ -0,0 +1,195 @@ += DESCRIPTION: + +Installs and configures Nagios 3 for a server and for clients using Chef 0.8 search capabilities. + += REQUIREMENTS: + +Requires Chef 0.8+ for search capability of roles and data bags. + +A data bag named 'users' should exist, see "DATA BAG" below. + +The monitoring server that uses this recipe should have a role named 'monitoring' or similar. A "per-environment" role should be created as well. See "ROLES" below. + +Because of the heavy use of search, this recipe will not work with Chef Solo, as it cannot do any searches without a server. + +== Platform: + +Tested on Ubuntu 9.04+ and Debian 5+. + +== Cookbooks: + +* apache2 + += ATTRIBUTES: + +Attributes under the 'nagios' namespace. + +== Client: + +The following attributes are used for the client NRPE checks for warning and critical levels. + +checks.memory.critical +checks.memory.warning +checks.load.critical +checks.load.warning +checks.smtp_host - default relayhost to check for connectivity. Default is an empty string, set via an attribute in a role. +server_role - the role that the nagios server will have in its run list that the clients can search for. + +== Server: + +dir - base server configuration directory. +log_dir - where the server logs. +cache_dir - cached directory. +docroot - DocumentRoot for webui. +config_subdir - for dropping in configurations as needed. +notifications_enabled - set to 1 to enable notification. +check_external_commands +default_contact_groups +sysadmin_email - default notification email. +sysadmin_sms_email - default notification sms. +server_auth_method - authentication with the server can be done with openid (using apache2::mod_auth_openid), or htauth (basic). The default is openid, any other value will use htauth (basic). +templates +interval_length - minimum interval. +default_host.check_interval +default_host.retry_interval +default_host.max_check_attempts +default_host.notification_interval +default_service.check_interval +default_service.retry_interval +default_service.max_check_attempts +default_service.notification_interval + += DATA BAGS: + +Create a `users` data bag that will contain the users that will be able to log into the Nagios webui. Each user can use htauth with a specified password, or an openid. Users that should be able to log in should be in the sysadmin group. Example user data bag item: + + { + "id": "nagiosadmin", + "groups": "sysadmin", + "htpasswd": "hashed_htpassword", + "openid": "http://nagiosadmin.myopenid.com/", + "nagios": { + "pager": "nagiosadmin_pager@example.com", + "email": "nagiosadmin@example.com" + } + } + +When using server_auth_method 'openid', use the openid in the data bag item. Any other value for this attribute (e.g., "htauth", "htpasswd", etc) will use the htpasswd value as the password in `/etc/nagios3/htpasswd.users`. + +The openid must have the http:// and trailing /. The htpasswd must be the hashed value. Get this value with: + + % htpasswd -n nagiosadmin + New password: + Re-type new password: + nagiosadmin:mbfzZrHsUUjds + +For example use the "mbfzZrHsUUjds" value in the data bag. + += ROLES: + +Create a role to use for the monitoring server. The role name should match the value of the attribute "nagios[:server_role]". By default, this is 'monitoring'. For example: + + % cat roles/monitoring.rb + name "monitoring" + description "Monitoring server" + run_list( + "recipe[nagios::server]" + ) + + default_attributes( + "nagios" => { + "server_auth_method" => "htauth" + } + ) + +Also create per-environment role. For example, production nodes: + + % cat roles/production.rb + name "production" + description "Nodes in the production environment." + default_attributes( + "app_environment" => "production" + ) + +Make sure to apply the production role to all nodes that should be monitored by the production monitoring server. + += USAGE: + +For a Nagios server, create a role named 'monitoring', and add the following recipe to the run_list: + + recipe[nagios::server] + +This will allow client nodes to search for the server by this role and add its IP address to the allowed list for NRPE. + +To install Nagios and NRPE on a client node: + + include_recipe "nagios::client" + +This is a fairly complicated cookbook. We'll describe the components in detail. + +== Definitions: + +nagios_conf:: This definition is used to drop in a configuration file in the base Nagios configuration directory's conf.d. This can be used for customized configurations for various services. + +== Libraries: + +default:: The library included with the cookbook provides some helper methods used in templates. + +* nagios_boolean +* nagios_interval - calculates interval based on interval length and a given number of seconds. +* nagios_attr - retrieves a nagios attribute from the node. + +== Recipes: + +=== Client + +The client recipe searches for allowed servers via a role named 'monitoring'. The recipe will also install the required packages and start the NRPE service. A custom plugin for checking memory is also added. + +Client commands for NRPE can be modified by editing the nrpe.cfg.erb template. + +=== Server + +The server recipe sets up Apache as the web front end. The nagios::client recipe is also included. This recipe also does a number of searches to dynamically build the hostgroups to monitor, hosts that belong to them and admins to notify of events/alerts. + +The recipe does the following: + +1. Searches for members of the sysadmins group by searching through 'users' data bag and adds them to a list for notification/contacts. +2. Search all nodes for a role matching the app_environment. +3. Search all available roles and build a list which will be the Nagios hostgroups. +4. Search for all nodes of each role and add the hostnames to the hostgroups. +5. Installs various packages required for the server. +6. Sets up some configuration directories. +7. Moves the package-installed Nagios configuration to a 'dist' directory. +8. Disables the 000-default site (present on Debian/Ubuntu Apache2 package installations). +9. Enables the Nagios web front end configuration. +10. Sets up the configuration templates for services, contacts, hostgroups and hosts. + +*NOTE*: You will probably need to change the services.cfg.erb template for your environment. + +To add custom commands for service checks, these can be done on a per-role basis by editing the 'services.cfg.erb' template. This template has some pre-configured checks that use role names used in an example infrastructure. Here's a brief description: + +* monitoring - check_smtp (e.g., postfix relayhost) w/ NRPE and tcp port 514 (e.g., rsyslog) +* load_balancer - check_nginx with NRPE. +* appserver - check_unicorn with NRPE, e.g. a Rails application using Unicorn. +* database_master - check_mysql_server with NRPE for a MySQL database master. + += LICENSE and AUTHOR: + +Author:: Joshua Sierles +Author:: Nathan Haneysmith +Author:: Joshua Timberman + +Copyright 2009, 37signals +Copyright 2009-2010, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/attributes/client.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/attributes/client.rb new file mode 100644 index 00000000000..ce687e4f27d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/attributes/client.rb @@ -0,0 +1,39 @@ +# +# Author:: Joshua Sierles +# Author:: Joshua Timberman +# Author:: Nathan Haneysmith +# Cookbook Name:: nagios +# Attributes:: client +# +# Copyright 2009, 37signals +# Copyright 2009-2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default[:nagios][:checks][:memory][:critical] = 150 +default[:nagios][:checks][:memory][:warning] = 250 +default[:nagios][:checks][:load][:critical] = "30,20,10" +default[:nagios][:checks][:load][:warning] = "15,10,5" +default[:nagios][:checks][:smtp_host] = String.new + +# default[:nagios][:server_role] = "monitoring" +default[:nagios][:server_role] = "nagios-server" + +default[:nagios][:user] = "nagios" +default[:nagios][:group]= "nagios" + +default[:nagios][:config] = {} +default[:nagios][:config][:environment] = "nagios-config-default" +default[:nagios][:config][:mode] = "full" + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/attributes/server.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/attributes/server.rb new file mode 100644 index 00000000000..cd11f7e6c4d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/attributes/server.rb @@ -0,0 +1,56 @@ +# +# Author:: Joshua Sierles +# Author:: Joshua Timberman +# Author:: Nathan Haneysmith +# Cookbook Name:: nagios +# Attributes:: server +# +# Copyright 2009, 37signals +# Copyright 2009-2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +set[:nagios][:dir] = "/etc/nagios3" +set[:nagios][:log_dir] = "/var/log/nagios3" +set[:nagios][:cache_dir] = "/var/cache/nagios3" +set[:nagios][:state_dir] = "/var/lib/nagios3" +set[:nagios][:docroot] = "/usr/share/nagios3/htdocs" +set[:nagios][:config_subdir] = "conf.d" + +default[:nagios][:notifications_enabled] = 0 +default[:nagios][:check_external_commands] = true +default[:nagios][:default_contact_groups] = %w(admins) +default[:nagios][:sysadmin_email] = "root@localhost" +default[:nagios][:sysadmin_sms_email] = "root@localhost" +default[:nagios][:server_auth_method] = "openid" + +# This setting is effectively sets the minimum interval (in seconds) nagios can handle. +# Other interval settings provided in seconds will calculate their actual from this value, since nagios works in 'time units' rather than allowing definitions everywhere in seconds + +default[:nagios][:templates] = Mash.new +default[:nagios][:interval_length] = 1 + +# Provide all interval values in seconds +default[:nagios][:default_host][:check_interval] = 15 +default[:nagios][:default_host][:retry_interval] = 15 +default[:nagios][:default_host][:max_check_attempts] = 1 +default[:nagios][:default_host][:notification_interval] = 300 + +default[:nagios][:default_service][:check_interval] = 60 +default[:nagios][:default_service][:retry_interval] = 15 +default[:nagios][:default_service][:max_check_attempts] = 3 +default[:nagios][:default_service][:notification_interval] = 1200 + +default[:nagios][:config] = {} +default[:nagios][:config][:environment] = "nagios-config-default" +default[:nagios][:config][:mode] = "full" diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/definitions/nagios_conf.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/definitions/nagios_conf.rb new file mode 100644 index 00000000000..23f0679592d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/definitions/nagios_conf.rb @@ -0,0 +1,40 @@ +# +# Author:: Joshua Sierles +# Author:: Joshua Timberman +# Author:: Nathan Haneysmith +# Cookbook Name:: nagios +# Definition:: nagios_conf +# +# Copyright 2009, 37signals +# Copyright 2009-2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +define :nagios_conf, :variables => {}, :config_subdir => true do + + subdir = if params[:config_subdir] + "/#{node[:nagios][:config_subdir]}/" + else + "/" + end + + template "#{node[:nagios][:dir]}#{subdir}#{params[:name]}.cfg" do + owner "nagios" + group "nagios" + source "#{params[:name]}.cfg.erb" + mode 0644 + variables params[:variables] + notifies :restart, resources(:service => "nagios3") + backup 0 + end +end diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.gd2 b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.gd2 new file mode 100644 index 00000000000..1da67773af4 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.gd2 differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.gif b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.gif new file mode 100644 index 00000000000..b35146cea37 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.gif differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.jpg b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.jpg new file mode 100644 index 00000000000..3d1f7d914e2 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.jpg differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.png b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.png new file mode 100644 index 00000000000..e626bcf3e9f Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/centos.png differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/munin-head.gif b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/munin-head.gif new file mode 100644 index 00000000000..3a08d7f8c48 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/munin-head.gif differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.gd2 b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.gd2 new file mode 100644 index 00000000000..e05163ba859 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.gd2 differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.gif b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.gif new file mode 100644 index 00000000000..0a51c5322e3 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.gif differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.jpg b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.jpg new file mode 100644 index 00000000000..0535df9515a Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.jpg differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.png b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.png new file mode 100644 index 00000000000..b2d71d93617 Binary files /dev/null and b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/icons/ubuntu.png differ diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/LICENSE.txt b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/LICENSE.txt new file mode 100644 index 00000000000..ef80403f520 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/LICENSE.txt @@ -0,0 +1,204 @@ +Applies to RabbitMQ checks: +From: https://github.com/jamesc/nagios-plugins-rabbitmq + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) 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. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check-mysql-slave.pl b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check-mysql-slave.pl new file mode 100644 index 00000000000..12fce3a4938 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check-mysql-slave.pl @@ -0,0 +1,158 @@ +#!/usr/bin/env perl + +# +# mysql-slave-check.pl +# +# Nagios script for checking the replication +# status of a slave MySQL server. +# +# Michal Ludvig (c) 2006 +# http://www.logix.cz/michal +# +# Run with --help to get some hints about usage or +# look at subroutine usage() near the end of this file. +# + +use strict; +use DBI; +use Getopt::Long; + +my $db_host = "localhost"; +my $db_port = "3306"; +my $db_sock = ""; +my $db_user = ""; +my $db_pass = ""; +my $db_name = ""; +my $warn = 300; +my $crit = 900; + +# Nagios codes +my %ERRORS=('OK'=>0, 'WARNING'=>1, 'CRITICAL'=>2, 'UNKNOWN'=>3, 'DEPENDENT'=>4); + +GetOptions( + 'host=s' => \$db_host, + 'port=i' => \$db_port, + 'user=s' => \$db_user, + 'password=s' => \$db_pass, + 'socket=s' => \$db_sock, + 'warn=i' => \$warn, + 'crit=i' => \$crit, + 'name|dbname|database=s' => \$db_name, + 'help' => sub { &usage(); }, +); + +&nagios_return("UNKNOWN", "Either set --host/--port or --sock, not both!") if (($db_port || $db_host ne "localhost") && $db_sock); + +my $db_conn_string = "DBI:mysql:"; +$db_conn_string .= "database=$db_name;"; +$db_conn_string .= "host=$db_host;"; +$db_conn_string .= "port=$db_port;"; +$db_conn_string .= "mysql_socket=$db_sock;"; + +## Connect to the database. +my $dbh = DBI->connect($db_conn_string, $db_user, $db_pass, + {'RaiseError' => 0, 'PrintError' => 0}); + +&nagios_return("UNKNOWN", "Connect failed: $DBI::errstr") if (!$dbh); + +## Now retrieve data from the table. +my $sth = $dbh->prepare("SHOW SLAVE STATUS"); +&nagios_return("UNKNOWN", "[1] $DBI::errstr") if (!$sth); + +$sth->execute(); + +&nagios_return("UNKNOWN", "[2] $DBI::errstr") if ($sth->err); +&nagios_return("CRITICAL", "Query returned ".scalar($sth->rows)." rows") if (scalar($sth->rows) < 1); + +## Query should return one row only +my $result = $sth->fetchrow_hashref(); + +&nagios_return("UNKNOWN", "[3] $DBI::errstr") if (!$result); + +## Print all results? No thanks. +# while (my ($key, $val) = each %$result) { +# print "$key = $val\n"; +# } + +## Check the returned values +&nagios_return("CRITICAL", "Slave_IO_Running=".$result->{'Slave_IO_Running'}."!") if ($result->{'Slave_IO_Running'} ne "Yes"); +&nagios_return("CRITICAL", "Slave_SQL_Running=".$result->{'Slave_SQL_Running'}."!") if ($result->{'Slave_SQL_Running'} ne "Yes"); +&nagios_return("CRITICAL", "Seconds_Behind_Master=".$result->{'Seconds_Behind_Master'}) if ($result->{'Seconds_Behind_Master'} > $crit); +&nagios_return("WARNING", "Seconds_Behind_Master=".$result->{'Seconds_Behind_Master'}) if ($result->{'Seconds_Behind_Master'} > $warn); +$sth->finish(); + +# Disconnect from the database. +$dbh->disconnect(); + +&nagios_return("OK", "$result->{'Slave_IO_State'}, replicating host $result->{'Master_Host'}:$result->{'Master_Port'}"); +exit 0; + +### + +sub nagios_return($$) { + my ($ret, $message) = @_; + my ($retval, $retstr); + if (defined($ERRORS{$ret})) { + $retval = $ERRORS{$ret}; + $retstr = $ret; + } else { + $retstr = 'UNKNOWN'; + $retval = $ERRORS{$retstr}; + $message = "WTF is return code '$ret'??? ($message)"; + } + $message = "$retstr - $message\n"; + print $message; + exit $retval; +} + +sub usage() { + print(" +Nagios script for checking the replication status of a +slave MySQL server. + +Michal Ludvig (c) 2006 + http://www.logix.cz/michal + + --host= Hostname or IP address to connect to. + --port= TCP port where the server listens + --socket= + Path and filename of the Unix socket + + --user= + --password= + Username and password of a user with + REPLICATION CLIENT privileges. See + below for details. + + --dbname= + Name od database to open on connect. + Should normaly not be needed. + + --warn= Warning threshold in seconds for Seconds_behind_master variable + + --crit= Critical threshold in seconds for Seconds_behind_master variable + + --help Guess what ;-) + +The script needs to connect as a MySQL user (say 'monitor') +with privilege REPLICATION CLIENT. Use this GRANT +command to create such a user: + +mysql> GRANT REPLICATION CLIENT ON *.* \\ + TO monitor\@localhost IDENTIFIED BY 'SecretPassword'; + +To access the script over SNMP put the following line +into your /etc/snmpd.conf: + +extend mysql-slave /path/to/mysql-slave-check.pl \\ + --user monitor --pass 'SecretPassword'; + +To check retrieve the status over SNMP use check_snmp_extend.sh +from http://www.logix.cz/michal/devel/nagios + + + +"); + exit 0; + +} diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_mem.sh b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_mem.sh new file mode 100644 index 00000000000..6dde99d84c6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_mem.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# +# evaluate free system memory from Linux based systems +# +# Date: 2007-11-12 +# Author: Thomas Borger - ESG +# +# the memory check is done with following command line: +# free -m | grep buffers/cache | awk '{ print $4 }' + +# get arguments + +while getopts 'w:c:hp' OPT; do + case $OPT in + w) int_warn=$OPTARG;; + c) int_crit=$OPTARG;; + h) hlp="yes";; + p) perform="yes";; + *) unknown="yes";; + esac +done + +# usage +HELP=" + usage: $0 [ -w value -c value -p -h ] + + syntax: + + -w --> Warning integer value + -c --> Critical integer value + -p --> print out performance data + -h --> print this help screen +" + +if [ "$hlp" = "yes" -o $# -lt 1 ]; then + echo "$HELP" + exit 0 +fi + +# get free memory +FMEM=`free -m | grep buffers/cache | awk '{ print $4 }'` + +# output with or without performance data +if [ "$perform" = "yes" ]; then + OUTPUTP="free system memory: $FMEM MB | free memory="$FMEM"MB;$int_warn;$int_crit;0" +else + OUTPUT="free system memory: $FMEM MB" +fi + +if [ -n "$int_warn" -a -n "$int_crit" ]; then + + err=0 + + if (( $FMEM <= $int_warn )); then + err=1 + elif (( $FMEM <= $int_crit )); then + err=2 + fi + + if (( $err == 0 )); then + + if [ "$perform" = "yes" ]; then + echo "MEM OK - $OUTPUTP" + exit "$err" + else + echo "MEM OK - $OUTPUT" + exit "$err" + fi + + elif (( $err == 1 )); then + if [ "$perform" = "yes" ]; then + echo "MEM WARNING - $OUTPUTP" + exit "$err" + else + echo "MEM WARNING - $OUTOUT" + exit "$err" + fi + + elif (( $err == 2 )); then + + if [ "$perform" = "yes" ]; then + echo "MEM CRITICAL - $OUTPUTP" + exit "$err" + else + echo "MEM CRITICAL - $OUTPUT" + exit "$err" + fi + + fi + +else + + echo "no output from plugin" + exit 3 + +fi +exit + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_mysqlhealth.pl b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_mysqlhealth.pl new file mode 100644 index 00000000000..f0391d5d779 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_mysqlhealth.pl @@ -0,0 +1,159 @@ +#!/usr/bin/perl +# -- +# Check the health of a mysql server. +# +# @author Peter Romianowski / optivo GmbH +# @version 1.0 2005-08-31 +# -- +use Getopt::Long; +use DBI; + +# -- +# Print out the usage message +# -- +sub usage { + print "usage: check_mysqlhealth.pl -H -u -p \n"; + print " Optional parameters:\n"; + print " --port \n"; + print " --Cc \n"; + print " --Wc \n"; + print " --Ca \n"; + print " --Wa \n"; + print " --sql \n"; +} + +$|=1; + +# -- +# Parse arguments and read Configuration +# -- +my ($host, $user, $password, $port, $criticalConnections, $warningConnections, $criticalActive, $warningActive, $sql); +GetOptions ( + 'host=s' => \$host, + 'H=s' => \$host, + 'user=s' => \$user, + 'u=s' => \$user, + 'password=s' => \$password, + 'p:s' => \$password, + 'port=i' => \$port, + 'Cc=i' => \$criticalConnections, + 'Wc=i' => \$warningConnections, + 'Ca=i' => \$criticalActive, + 'Wa=i' => \$warningActive, + 'sql=s'=> \$sql +); + +if (!$host || !$user) { + usage(); + exit(1); +} + +if (!$port) { + $port = 3306; +} + +my $totalTime = time(); + +# -- +# Establish connection +# -- +my $state = "OK"; +my $dbh; +eval { + $dbh = DBI->connect("DBI:mysql:host=$host;port=$port", $user, $password, {'RaiseError' => 1}); +}; + +if ($@) { + my $status = $@; + print 'CRITICAL: Connect failed with reason ' . $status . "\n"; + exit 2; +} + +# -- +# Count active statements +# -- +my $connections = 0; +my $active = 0; + +eval { + my $sth = $dbh->prepare("SHOW PROCESSLIST"); + $sth->execute(); + my $row; + do { + $row = $sth->fetchrow_hashref(); + if ($row) { + if (!($row->{'Command'} =~ /Sleep/)) { + $active++; + } + $connections++; + } + + } while ($row); +}; + +if ($@) { + my $status = $@; + print 'CRITICAL: Error executing SHOW PROCESSLIST, reason ' . $status . "\n"; + exit 2; +} + +# -- +# Execute optional sql statement if given +# -- +$sqlResult; +if ($sql) { + eval { + my $sth = $dbh->prepare($sql); + $sth->execute(); + my @row = $sth->fetchrow(); + if (@row) { + $sqlResult = join('|', @row); + } + }; + + if ($@) { + my $status = $@; + print 'CRITICAL: Error executing statement "' . $sql . '", reason ' . $status . "\n"; + exit 2; + } + +} + +# -- +# Cleanup resources +# -- +$dbh->disconnect(); + +# -- +# Check +# -- +my $statusString = "$connections connections, $active active connections"; +if ($criticalConnections && $criticalConnections < $connections) { + print "CRITICAL: Number of connections higher than $criticalConnections ($statusString)\n"; + exit 2; +} +if ($criticalActive && $criticalActive < $active) { + print "CRITICAL: Number of ACTIVE connections higher than $criticalActive ($statusString)\n"; + exit 2; +} +if ($warningConnections && $warningConnections < $connections) { + print "WARNING: Number of connections higher than $warningConnections ($statusString)\n"; + exit 1; +} +if ($warningActive && $warningActive < $active) { + print "WARNING: Number of ACTIVE connections higher than $warningActive ($statusString)\n"; + exit 1; +} +if ($sql) { + if ($sqlResult) { + $statusString = "Statement '$sql' returned: '$sqlResult', $statusString"; + } else { + print "CRITICAL: Execution of statement '$sql' did not produce any result ($statusString)\n"; + exit 2; + } +} + +print "OK: $statusString\n"; +exit 0; + + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_nova_manage b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_nova_manage new file mode 100755 index 00000000000..3e78457a606 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_nova_manage @@ -0,0 +1,94 @@ +#!/bin/bash +# +# check to see that nova services are running within options +# + +# get arguments + +int_warn=1 +int_crit=2 +int_compute=0 +int_volume=0 +int_network=0 +int_scheduler=0 + +while getopts 'hS:N:C:V:w:c:' OPT; do + case $OPT in + w) int_warn=$OPTARG;; + c) int_crit=$OPTARG;; + C) int_compute=$OPTARG;; + V) int_volume=$OPTARG;; + N) int_network=$OPTARG;; + S) int_scheduler=$OPTARG;; + h) hlp="yes";; + *) unknown="yes";; + esac +done + +# usage +HELP=" +Usage: $0 [ -w value ] [ -c value ] [ -C value ] [ -N value ] [ -S value ] [ -V value ] +Options: + -w --> Warning threshold of missing components (default: 1) + -c --> Critical threshold of missing components (default: 2) + -C --> Expected Compute service entries (default: 0) + -N --> Expected Network service entries (default: 0) + -S --> Expected Scheduler service entries (default: 0) + -V --> Expected Volume service entries (default: 0) + -h --> print this help screen +" + +if [ "$hlp" = "yes" ]; then + echo "$HELP" + exit 0 +fi + +# Get nova status +nova-manage service list > /tmp/nova.out.$$ +N_N=`grep nova-network /tmp/nova.out.$$ | grep -v XXX | wc -l` +N_C=`grep nova-compute /tmp/nova.out.$$ | grep -v XXX | wc -l` +N_V=`grep nova-volume /tmp/nova.out.$$ | grep -v XXX | wc -l` +N_S=`grep nova-scheduler /tmp/nova.out.$$ | grep -v XXX | wc -l` + +D_C=`expr $int_compute - $N_C` +if [ $D_C -lt 0 ]; then + D_C=0 +fi +D_N=`expr $int_network - $N_N` +if [ $D_N -lt 0 ]; then + D_N=0 +fi +D_V=`expr $int_volume - $N_V` +if [ $D_V -lt 0 ]; then + D_V=0 +fi +D_S=`expr $int_scheduler - $N_S` +if [ $D_S -lt 0 ]; then + D_S=0 +fi + +D_Total=`expr $D_N + $D_S + $D_C + $D_V` + +err=0 +if (( $D_Total >= $int_crit )); then + err=2 +elif (( $D_Total >= $int_warn )); then + err=1 +fi + +OUTPUTP="scheduler: $N_S ($int_scheduler), network: $N_N ($int_network), compute: $N_C ($int_compute), volume: $N_V ($int_volume)" + +if (( $err == 0 )); then + echo "Nova OK - $OUTPUTP" + exit "$err" +elif (( $err == 1 )); then + echo "Nova WARNING ($D_Total) - $OUTPUTP" + exit "$err" +elif (( $err == 2 )); then + echo "Nova CRITICAL ($D_Total) - $OUTPUTP" + exit "$err" +fi + +echo "Nova UNKNOWN - Bad Code Path" +exit -1 + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_aliveness b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_aliveness new file mode 100755 index 00000000000..b104a6847c9 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_aliveness @@ -0,0 +1,222 @@ +#!/usr/bin/env perl + +### check_rabbitmq_aliveness.pl + +# Use the management aliveness-check to send/receive a message through a vhost + +# Originally by Nathan Vonnahme, n8v at users dot sourceforge +# dot net, July 19 2006 + +############################################################################## +# prologue +use strict; +use warnings; + +use Nagios::Plugin ; +use LWP::UserAgent; +use URI::Escape; +use JSON; + +use vars qw($VERSION $PROGNAME $verbose $timeout); +$VERSION = '1.0'; + +# get the base name of this script for use in the examples +use File::Basename; +$PROGNAME = basename($0); + + +############################################################################## +# define and get the command line options. +# see the command line option guidelines at +# http://nagiosplug.sourceforge.net/developer-guidelines.html#PLUGOPTIONS + + +# Instantiate Nagios::Plugin object (the 'usage' parameter is mandatory) +my $p = Nagios::Plugin->new( + usage => "Usage: %s [options] -H hostname", + license => "", + version => $VERSION, + blurb => 'This plugin uses the RabbitMQ management aliveness-check to send/receive a message through a vhost.', +); + +$p->add_arg(spec => 'host|H=s', + help => "Specify the host to connect to", + required => 1 +); +$p->add_arg(spec => 'port=i', + help => "Specify the port to connect to (default: %s)", + default => 55672 +); + +$p->add_arg(spec => 'user|u=s', + help => "Username (default: %s)", + default => "guest", +); +$p->add_arg(spec => 'password|p=s', + help => "Password (default: %s)", + default => "guest" +); + +$p->add_arg(spec => 'vhost=s', + help => "Specify the vhost to test (default: %s)", + default => "/" +); +# Parse arguments and process standard ones (e.g. usage, help, version) +$p->getopts; + + +# perform sanity checking on command line options + + +############################################################################## +# check stuff. + +my $hostname=$p->opts->host; +my $port=$p->opts->port; +my $vhost=uri_escape($p->opts->vhost); + +my $url = "http://$hostname:$port/api/aliveness-test/$vhost"; + +my $ua = LWP::UserAgent->new(env_proxy=>1); +$ua->agent($PROGNAME.' '); +$ua->timeout($p->opts->timeout); +$ua->credentials("$hostname:$port", + "Management: Web UI", $p->opts->user, $p->opts->password); +my $req = HTTP::Request->new(GET => $url); +my $res = $ua->request($req); + +if (!$res->is_success) { + # Deal with standard error conditions - make the messages more sensible + if ($res->code == 400) { + my $bodyref = decode_json $res->content; + $p->nagios_exit(CRITICAL, $bodyref->{'reason'}); + } + $res->code == 404 and $p->nagios_die("Not found: ".$p->opts->vhost); + $res->code == 401 and $p->nagios_die("Access refused: ".$p->opts->vhost); + if ($res->code < 200 or $res->code > 400 ) { + $p->nagios_exit(CRITICAL, "Received ".$res->status_line." for vhost: ".$p->opts->vhost); + } +} + +my $bodyref = decode_json $res->content; +$bodyref->{'status'} eq "ok" or $p->nagios_exit(CRITICAL, $res->content); +my($code, $message) = (OK, "vhost: ".$p->opts->vhost); +$p->nagios_exit( + return_code => $code, + message => $message +); + +=head1 NAME + +check_rabbitmq_aliveness - Nagios plugin using RabbitMQ management API to +check liveness by send/receive a message through a vhost + +=head1 SYNOPSIS + +check_rabbitmq_aliveness [options] -H hostname + +=head1 DESCRIPTION + +Use the management interface of RabbitMQ to check that the server is alive. +It declares a test queue, then publishes and consumes a message. + +It uses Nagios::Plugin and accepts all standard Nagios options. + +=head1 OPTIONS + +=over + +=item -h | --help + +Display help text + +=item -v | --verbose + +Verbose output + +=item -t | --timeout + +Set a timeout for the check in seconds + +=item -H | --host + +The host to connect to + +=item --port + +The port to connect to (default: 55672) + +=item --vhost + +The vhost to create the test queue within (default: /) + +=item --user + +The user to connect as (default: guest) + +=item --pass + +The password for the user (default: guest) + +=back + +=head1 EXAMPLES + +The defaults all work with a standard fresh install of RabbitMQ, and all that +is needed is to specify the host to connect to: + + check_rabbitmq_aliveness -H rabbit.example.com + +This returns a standard Nagios result: + + RABBITMQ_ALIVENESS OK - vhost: / + +You can choose a different vhost to use for the check also: + + check_rabbitmq_aliveness -H rabbit.example.com --vhost /foo + +=head1 ERRORS + +The check tries to provide useful error messages on the status line for +standard error conditions. + +Otherwise it returns the HTTP Error message returned by the management +interface. + +=head1 EXIT STATUS + +Returns zero if check is OK otherwise returns standard Nagios exit codes to +signify WARNING, UNKNOWN or CRITICAL state. + +=head1 SEE ALSO + +See Nagios::Plugin(3) + +The RabbitMQ management plugin is described at +http://www.rabbitmq.com/management.html + +=head1 LICENSE + +This file is part of nagios-plugins-rabbitmq. + +Copyright 2010, Platform 14. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=head1 AUTHOR + +James Casey + +=cut + +1; \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_objects b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_objects new file mode 100755 index 00000000000..e11a42ca15d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_objects @@ -0,0 +1,230 @@ +#!/usr/bin/env perl +# +# check_rabbitmq_objects +# +# Use the management APIs to count various server objects - +# vhost, exchange, binding, queue, channel +# +use strict; +use warnings; + +use Nagios::Plugin ; +use LWP::UserAgent; +use URI::Escape; +use JSON; + +use vars qw($VERSION $PROGNAME $verbose $timeout); +$VERSION = '1.0'; + +# get the base name of this script for use in the examples +use File::Basename; +$PROGNAME = basename($0); + +my $p = Nagios::Plugin->new( + usage => "Usage: %s [options] -H hostname", + license => "", + version => $VERSION, + blurb => 'This plugin uses the RabbitMQ management to count various objects.', +); + +$p->add_arg(spec => 'host|H=s', + help => "Specify the host to connect to", + required => 1 +); +$p->add_arg(spec => 'port=i', + help => "Specify the port to connect to (default: %s)", + default => 55672 +); + +$p->add_arg(spec => 'user|u=s', + help => "Username (default: %s)", + default => "guest", +); +$p->add_arg(spec => 'password|p=s', + help => "Password (default: %s)", + default => "guest" +); + + +# Parse arguments and process standard ones (e.g. usage, help, version) +$p->getopts; + + +# perform sanity checking on command line options + + +############################################################################## +# check stuff. + +my $hostname=$p->opts->host; +my $port=$p->opts->port; + +my $ua = LWP::UserAgent->new(env_proxy=>1); +$ua->agent($PROGNAME.' '); +$ua->timeout($p->opts->timeout); +$ua->credentials("$hostname:$port", + "Management: Web UI", $p->opts->user, $p->opts->password); + +my @calls = ("vhost", "exchange", "binding", "queue", "channel"); +for my $call (@calls) { + my $url = sprintf("http://%s:%d/api/%ss", $hostname, $port, $call); + my ($code, $result) = request($url); + if ($code != 200) { + $p->nagios_exit(CRITICAL, "$result : /api/". $call."s"); + } + # we're always returned a list - count it + my $count = $#{$result}; + $count = 0 if $count == -1; + $p->add_message(OK, sprintf("$call (%.2f%%)", $count)) ; + $p->add_perfdata(label=>$call, value => $count); + +} + +$p->nagios_exit(return_code => OK, message => "Gathered Object Counts"); + +sub request { + my ($url) = @_; + my $req = HTTP::Request->new(GET => $url); + my $res = $ua->request($req); + + if (!$res->is_success) { + # Deal with standard error conditions - make the messages more sensible + if ($res->code == 400) { + my $bodyref = decode_json $res->content; + return (400, $bodyref->{'reason'}); + + } + $res->code == 404 and return (404, "Not Found"); + $res->code == 401 and return (401, "Access Refused"); + $res->status_line =~ /Can\'t connect/ and return (500, "Connection Refused : $url"); + if ($res->code < 200 or $res->code > 400 ) { + return ($res->code, "Received ".$res->status_line); + } + } + my $bodyref = decode_json $res->content; + return($res->code, $bodyref); +} + +=head1 NAME + +check_rabbitmq_objects - Nagios plugin using RabbitMQ management API to +count the number of various broker objects + +=head1 SYNOPSIS + +check_rabbitmq_objects [options] -H hostname + +=head1 DESCRIPTION + +Use the management interface of RabbitMQ to count the number of various +broker objects. These are published as performance metrics for the check. + +Currently the following objects are counted: + +=over + +=item * vhost + +=item * exchange + +=item * binding + +=item * queue + +=item * channel + +=back + +It uses Nagios::Plugin and accepts all standard Nagios options. + +=head1 OPTIONS + +=over + +=item -h | --help + +Display help text + +=item -v | --verbose + +Verbose output + +=item -t | --timeout + +Set a timeout for the check in seconds + +=item -H | --host + +The host to connect to + +=item --port + +The port to connect to (default: 55672) + +=item --user + +The user to connect as (default: guest) + +=item --pass + +The password for the user (default: guest) + +=back + +=head1 EXAMPLES + +The defaults all work with a standard fresh install of RabbitMQ, and all that +is needed is to specify the host to connect to: + + check_rabbitmq_objects -H rabbit.example.com + +This returns a standard Nagios result: + + RABBITMQ_OBJECTS OK - Gathered Object Counts | vhost=1;; exchange=7;; + binding=2;; queue=1;; channel=0;; + +=head1 ERRORS + +The check tries to provide useful error messages on the status line for +standard error conditions. + +Otherwise it returns the HTTP Error message returned by the management +interface. + +=head1 EXIT STATUS + +Returns zero if check is OK otherwise returns standard Nagios exit codes to +signify WARNING, UNKNOWN or CRITICAL state. + +=head1 SEE ALSO + +See Nagios::Plugin(3) + +The RabbitMQ management plugin is described at +http://www.rabbitmq.com/management.html + +=head1 LICENSE + +This file is part of nagios-plugins-rabbitmq. + +Copyright 2010, Platform 14. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=head1 AUTHOR + +James Casey + +=cut + +1; \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_overview b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_overview new file mode 100755 index 00000000000..32f871346e6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_overview @@ -0,0 +1,271 @@ +#!/usr/bin/env perl +# +# check_rabbitmq_overview +# +# Use the management APIs to count pending messages in broker +# +use strict; +use warnings; + +use Nagios::Plugin qw(%STATUS_TEXT OK CRITICAL WARNING UNKNOWN); +use LWP::UserAgent; +use URI::Escape; +use JSON; + +use vars qw($VERSION $PROGNAME $verbose $timeout); +$VERSION = '1.0'; + +# get the base name of this script for use in the examples +use File::Basename; +$PROGNAME = basename($0); + +my $p = Nagios::Plugin->new( + usage => "Usage: %s [options] -H hostname", + license => "", + version => $VERSION, + blurb => 'This plugin uses the RabbitMQ management to count messages in server.', +); + +$p->add_arg(spec => 'host|H=s', + help => "Specify the host to connect to", + required => 1 +); +$p->add_arg(spec => 'port=i', + help => "Specify the port to connect to (default: %s)", + default => 55672 +); + +$p->add_arg(spec => 'user|u=s', + help => "Username (default: %s)", + default => "guest", +); +$p->add_arg(spec => 'password|p=s', + help => "Password (default: %s)", + default => "guest" +); + +$p->add_arg( + spec => 'warning|w=s', + help => +qq{-w, --warning=INTEGER,INTEGER,INTEGER + Warning thresholds specified in order that the metrics are returned. + Specify -1 if no warning threshold.}, + +); + +$p->add_arg( + spec => 'critical|c=s', + help => +qq{-c, --critical=INTEGER,INTEGER,INTEGER + Critical thresholds specified in order that the metrics are returned. + Specify -1 if no critical threshold.}, +); + +# Parse arguments and process standard ones (e.g. usage, help, version) +$p->getopts; + + +# perform sanity checking on command line options +my %warning; +if (defined $p->opts->warning) { + my @warning = split(',', $p->opts->warning); + $p->nagios_die("You should specify three ranges for --warning argument") unless $#warning == 2; + + $warning{'messages'} = shift @warning; + $warning{'messages_ready'} = shift @warning; + $warning{'messages_unacknowledged'} = shift @warning; +} + +my %critical; +if (defined $p->opts->critical) { + my @critical = split(',', $p->opts->critical); + $p->nagios_die("You should specify three ranges for --critical argument") unless $#critical == 2; + + $critical{'messages'} = shift @critical; + $critical{'messages_ready'} = shift @critical; + $critical{'messages_unacknowledged'} = shift @critical; +} + + +############################################################################## +# check stuff. + +my $hostname=$p->opts->host; +my $port=$p->opts->port; + +my $ua = LWP::UserAgent->new(env_proxy=>1); +$ua->agent($PROGNAME.' '); +$ua->timeout($p->opts->timeout); +$ua->credentials("$hostname:$port", + "Management: Web UI", $p->opts->user, $p->opts->password); + + +my $url = sprintf("http://%s:%d/api/overview", $hostname, $port); +my ($retcode, $result) = request($url); +if ($retcode != 200) { + $p->nagios_exit(CRITICAL, "$result : /api/overview"); +} + +my @metrics = ( "messages", "messages_ready", "messages_unacknowledged"); +for my $metric (@metrics) { + my $warning = undef; + $warning = $warning{$metric} if (defined $warning{$metric} and $warning{$metric} != -1); + my $critical = undef; + $critical = $critical{$metric} if (defined $critical{$metric} and $critical{$metric} != -1); + + my $value = $result->{'queue_totals'}->{$metric}; + my $code = $p->check_threshold(check => $value, warning => $warning, critical=> $critical); + $p->add_message($code, sprintf("$metric ".$STATUS_TEXT{$code}." (%d)", $value)) ; + $p->add_perfdata(label=>$metric, value => $value, warning=>$warning, critical=> $critical); +} + +my ($code, $message) = $p->check_messages(join_all=>', '); +$p->nagios_exit(return_code => $code, message => $message); + + +sub request { + my ($url) = @_; + my $req = HTTP::Request->new(GET => $url); + my $res = $ua->request($req); + + if (!$res->is_success) { + # Deal with standard error conditions - make the messages more sensible + if ($res->code == 400) { + my $bodyref = decode_json $res->content; + return (400, $bodyref->{'reason'}); + + } + $res->code == 404 and return (404, "Not Found"); + $res->code == 401 and return (401, "Access Refused"); + $res->status_line =~ /Can\'t connect/ and return (500, "Connection Refused : $url"); + if ($res->code < 200 or $res->code > 400 ) { + return ($res->code, "Received ".$res->status_line); + } + } + my $bodyref = decode_json $res->content; + return($res->code, $bodyref); +} + +=head1 NAME + +check_rabbitmq_overview - Nagios plugin using RabbitMQ management API to +count the messages pending on the broker + +=head1 SYNOPSIS + +check_rabbitmq_overview [options] -H hostname + +=head1 DESCRIPTION + +Use the management interface of RabbitMQ to count the number of pending, +ready and unacknowledged messages. These are published as performance +metrics for the check. + +Critical and warning thresholds can be set for each of the metrics. + +It uses Nagios::Plugin and accepts all standard Nagios options. + +=head1 OPTIONS + +=over + +=item -h | --help + +Display help text + +=item -v | --verbose + +Verbose output + +=item -t | --timeout + +Set a timeout for the check in seconds + +=item -H | --host + +The host to connect to + +=item --port + +The port to connect to (default: 55672) + +=item --user + +The user to connect as (default: guest) + +=item --pass + +The password for the user (default: guest) + +=item -w | --warning + +The warning levels for each count of messages, messages_ready and +messages_unacknowledged. This field consists of three comma-separated +integers. Specify -1 if no threshold for a particular count. + +=item -c | --critical + +The critical levels for each count of messages, messages_ready and +messages_unacknowledged. This field consists of three comma-separated +integers. Specify -1 if no threshold for a particular count + +=back + +=head1 EXAMPLES + +The defaults all work with a standard fresh install of RabbitMQ, and all that +is needed is to specify the host to connect to: + + check_rabbitmq_overview -H rabbit.example.com + +This returns a standard Nagios result: + + RABBITMQ_OVERVIEW OK - messages OK (25794) messages_ready OK (22971) + messages_unacknowledged OK (2823) | messages=25794;; + messages_ready=22971;; messages_unacknowledged=2823;; + +=head1 ERRORS + +The check tries to provide useful error messages on the status line for +standard error conditions. + +Otherwise it returns the HTTP Error message returned by the management +interface. + +=head1 EXIT STATUS + +Returns zero if check is OK otherwise returns standard Nagios exit codes to +signify WARNING, UNKNOWN or CRITICAL state. + +=head1 SEE ALSO + +See Nagios::Plugin(3) + +The RabbitMQ management plugin is described at +http://www.rabbitmq.com/management.html + +=head1 LICENSE + +This file is part of nagios-plugins-rabbitmq. + +Copyright 2010, Platform 14. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=head1 AUTHOR + +James Casey + +=cut + +1; \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_server b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_server new file mode 100755 index 00000000000..ae1c684cc6d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_rabbitmq_server @@ -0,0 +1,288 @@ +#!/usr/bin/env perl + +### check_rabbitmq_aliveness.pl + +# Use the management overview to check server statistics + +############################################################################## +# prologue +use strict; +use warnings; + +use Nagios::Plugin qw(%STATUS_TEXT OK CRITICAL WARNING UNKNOWN); +use LWP::UserAgent; +use URI::Escape; +use JSON; + +use Data::Dumper; + +use vars qw($VERSION $PROGNAME $verbose $timeout $code $message); +$VERSION = '1.0'; + +# get the base name of this script for use in the examples +use File::Basename; +$PROGNAME = basename($0); + + +############################################################################## +# define and get the command line options. +# see the command line option guidelines at +# http://nagiosplug.sourceforge.net/developer-guidelines.html#PLUGOPTIONS + + +# Instantiate Nagios::Plugin object (the 'usage' parameter is mandatory) +my $p = Nagios::Plugin->new( + usage => "Usage: %s [options] -H hostname", + license => "", + version => $VERSION, + blurb => 'This plugin uses the RabbitMQ management node API to check server process parameters.', +); + +$p->add_arg(spec => 'host|H=s', + help => "Specify the host to connect to", + required => 1 +); +$p->add_arg(spec => 'port|p=i', + help => "Specify the port to connect to (default: %s)", + default => 55672 +); + +$p->add_arg( + spec => 'warning|w=s', + + help => +qq{-w, --warning=INTEGER,INTEGER,INTEGER + Warning thresholds specified in order that the metrics are returned. + (Default : %s)}, +# required => 1, + default => "80,80,80", +); + +$p->add_arg( + spec => 'critical|c=s', + help => +qq{-c, --critical=INTEGER,INTEGER,INTEGER + Warning thresholds specified in order that the metrics are returned. + (Default: %s) }, + default => "90,90,90", +); + +$p->add_arg(spec => 'user|u=s', + help => "Username (default: %s)", + default => "guest", +); +$p->add_arg(spec => 'password|p=s', + help => "Password (default: %s)", + default => "guest" +); + +# Parse arguments and process standard ones (e.g. usage, help, version) +$p->getopts; + + +# Check we have three values for warning and critical thresholds +my @warning = split(',', $p->opts->warning); +$p->nagios_die("You should specify three ranges for --warning argument") unless $#warning == 2; + +my @critical = split(',', $p->opts->critical); +$p->nagios_die("You should specify three ranges for --critical argument") unless $#critical == 2; + +############################################################################## +# check stuff. + +my $hostname = $p->opts->host; +$hostname =~ /^([a-zA-Z0-9-]*)/; +my $shortname=$1; +my $port = $p->opts->port; + +my $path = "nodes/rabbit\@$shortname"; +my $url = "http://$hostname:$port/api/$path"; + +my $ua = LWP::UserAgent->new(env_proxy=>1); +$ua->agent($PROGNAME.' '); +$ua->timeout($p->opts->timeout); +$ua->credentials("$hostname:$port", + "Management: Web UI", $p->opts->user, $p->opts->password); +my $req = HTTP::Request->new(GET => $url); +my $res = $ua->request($req); + +if (!$res->is_success) { + # Deal with standard error conditions - make the messages more sensible + if ($res->code == 400) { + my $bodyref = decode_json $res->content; + $p->nagios_exit(CRITICAL, $bodyref->{'reason'}); + } + $res->code == 404 and $p->nagios_die("Not found: ".$path); + $res->code == 401 and $p->nagios_die("Access refused: ".$path); + if ($res->code < 200 or $res->code > 400 ) { + $p->nagios_exit(CRITICAL, "Received ".$res->status_line." for path: ".$path); + } +} + +my $bodyref = decode_json $res->content; +check($p, "Memory", $bodyref->{'mem_used'}, $bodyref->{'mem_limit'}, $warning[0], $critical[0]); +check($p, "Process", $bodyref->{'proc_used'}, $bodyref->{'proc_total'}, $warning[1], $critical[1]); +check($p, "FD", $bodyref->{'fd_used'}, $bodyref->{'fd_total'}, $warning[2], $critical[2]); + +($code, $message) = $p->check_messages(join_all=>', '); +$p->nagios_exit( return_code=>$code, message=>$message); + + +sub check { + my $p = shift; + my $label = shift; + my $used = shift; + my $limit = shift; + my $warning = shift; + my $critical = shift; + + my $value = percent($used, $limit); + my $code = $p->check_threshold(check => $value, warning => $warning, critical => $critical); + $p->add_message($code, sprintf("$label ".$STATUS_TEXT{$code}." (%.2f%%)", $value)) ; + $p->add_perfdata(label=>$label, value => $value, uom=>"%", warning=>$warning, critical=>$critical); +} + +sub percent { + my $num = shift; + my $denom = shift; + return (10000 * $num/ $denom)%1000/100; +} + +sub structured { + my $content = shift; + $Data::Dumper::Terse = 1; # don't output names where feasible + $Data::Dumper::Indent = 2; + return Dumper($content); +} + +=head1 NAME + +check_rabbitmq_server - Nagios plugin using RabbitMQ management API to +check the server resource usage (processes, memory and file descriptors) + +=head1 SYNOPSIS + +check_rabbitmq_server [options] -H hostname + +=head1 DESCRIPTION + +Use the management interface of RabbitMQ to check the resource usage of the +server. This check looks at the node statistics for the rabbit node on +the host and examines the erlang memory, process and file descriptor usage. + +It provides performance data for each of these variables and allows for +warning and criticality levels to be specified for each. + +It uses Nagios::Plugin and accepts all standard Nagios options. + +=head1 OPTIONS + +=over + +=item -h | --help + +Display help text + +=item -v | --verbose + +Verbose output + +=item -t | --timeout + +Set a timeout for the check in seconds + +=item -H | --host + +The host to connect to + +=item --port + +The port to connect to (default: 55672) + +=item -w | --warning + +The warning levels, expressed as a percentage for each of memory, process +and file descriptor usage. This field consists of three comma-separated +integers. (default: 90,90,90) + +=item -c | --critical + +The critical levels, expressed as a percentage for each of memory, process +and file descriptor usage. This field consists of three comma-separated +integers. (default: 90,90,90) + +=item --user + +The user to connect as (default: guest) + +=item --pass + +The password for the user (default: guest) + +=back + +=head1 EXAMPLES + +The defaults all work with a standard fresh install of RabbitMQ, and all that +is needed is to specify the host to connect to: + + check_rabbitmq_server -H rabbit.example.com + +This returns a standard Nagios result: + + RABBITMQ_SERVER OK - Memory OK (6.19%) Process OK (0.01%) + FD OK (2.53%) | Memory=6.19%;80;90 Process=0.01%;80;90 FD=2.53%;80;90 + +You can specify different warning and criticality levels. B<-w> and B<-c> +both accept three comma-separated percentages which represent the +thresholds for memory, process and file descriptor usage respectively. For +example, to specify warnings for memory at 60%, process at 80% and file +descriptors at 95%: + + check_rabbitmq_server -H rabbit.example.com -w 60,80,95 + +=head1 ERRORS + +The check tries to provide useful error messages on the status line for +standard error conditions. + +Otherwise it returns the HTTP Error message returned by the management +interface. + +=head1 EXIT STATUS + +Returns zero if check is OK otherwise returns standard Nagios exit codes to +signify WARNING, UNKNOWN or CRITICAL state. + +=head1 SEE ALSO + +See Nagios::Plugin(3) + +The RabbitMQ management plugin is described at +http://www.rabbitmq.com/management.html + +=head1 LICENSE + +This file is part of nagios-plugins-rabbitmq. + +Copyright 2010, Platform 14. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +=head1 AUTHOR + +James Casey + +=cut + +1; \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_solr.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_solr.rb new file mode 100644 index 00000000000..e2689f0bb15 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/check_solr.rb @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby +# +# Nagios check for Solr server health +# Copyright 37signals, 2008 +# Author: Joshua Sierles (joshua@37signals.com) + +require 'rubygems' +require 'xmlsimple' +require 'net/http' +require 'uri' +require 'choice' + +EXIT_OK = 0 +EXIT_WARNING = 1 +EXIT_CRITICAL = 2 +EXIT_UNKNOWN = 3 + +Choice.options do + header '' + header 'Specific options:' + + option :warn do + short '-w' + long '--warning=VALUE' + desc 'Warning threshold' + cast Integer + end + + option :crit do + short '-c' + long '--critical=VALUE' + desc 'Critical threshold' + cast Integer + end + + option :host do + short '-h' + long '--host=VALUE' + desc 'Solr host' + end + + option :prefix do + short '-p' + long '--prefix=VALUE' + desc 'App prefix' + end + + option :start do + short '-s' + long '--start=VALUE' + desc 'Start index' + end + + option :rows do + short '-r' + long '--rows=VALUE' + desc 'Number of rows to check for' + default 10 + end + + option :query do + short '-q' + long '--query=VALUE' + desc 'Query term to search for' + default "test%0D%0A" + end + + option :version do + short '-v' + long '--version=VALUE' + desc 'Specify version' + default "2.2" + end +end + +c = Choice.choices + +message = "Solr reports %d rows" + +if c[:crit] + + value = 0 + begin + url = URI.parse("http://#{c[:host]}:8983/") + res = Net::HTTP.start(url.host, url.port) do |http| + http.get("/#{c[:prefix]}/select/?q=#{c[:query]}&version=#{c[:version]}&start=#{c[:start]}&rows=#{c[:rows]}&indent=on") + end + + solr = XmlSimple.xml_in(res.body, { 'ForceArray' => false, 'KeepRoot' => false }) + solr['lst']['lst']['str'].each do |str| + next unless str['name'] == 'rows' + value = str['content'].to_i + end + rescue Exception => e + puts "Error checking Solr: #{e.message}" + exit(EXIT_UNKNOWN) + end + + + if value != c[:crit] + puts sprintf(message, value) + exit(EXIT_CRITICAL) + end + + if c[:warn] && value >= c[:warn] + puts sprintf(message, value) + exit(EXIT_WARNING) + end + +else + puts "Please provide a critical threshold" + exit +end + +# if warning nor critical trigger, say OK and return performance data + +puts sprintf("Solr is OK, reports %d rows", value) \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/db_queues.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/db_queues.rb new file mode 100644 index 00000000000..7666b71ed3f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/db_queues.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env ruby +# +# Check the size of a database queue +# + +require 'rubygems' +require 'choice' +require 'mysql' + +EXIT_OK = 0 +EXIT_WARNING = 1 +EXIT_CRITICAL = 2 +EXIT_UNKNOWN = 3 + +Choice.options do + header '' + header 'Specific options:' + + option :warn do + short '-w' + long '--warning=VALUE' + desc 'Warning threshold' + cast Integer + end + + option :crit do + short '-c' + long '--critical=VALUE' + desc 'Critical threshold' + cast Integer + end + + option :host do + short '-H' + long '--host=VALUE' + desc 'MySQL DB host' + end + + option :username do + short '-u' + long '--username=VALUE' + desc 'MySQL DB username' + end + + option :password do + short '-p' + long '--password=VALUE' + desc 'MySQL DB password' + end + + option :database do + short '-d' + long '--database=VALUE' + desc 'MySQL database' + end + + option :query do + short '-q' + long '--query=VALUE' + desc 'MySQL DB count query' + end + +end + +c = Choice.choices + +# nagios performance data format: 'label'=value[UOM];[warn];[crit];[min];[max] +# see http://nagiosplug.sourceforge.net/developer-guidelines.html#AEN203 + +perfdata = "query_count=%d;#{c[:warn]};#{c[:crit]}" +message = "Query '#{c[:query]}' result %d exceeds %d|#{perfdata}" + +if c[:warn] && c[:crit] + + conn = Mysql::connect(c[:host], c[:username], c[:password], c[:database]) + res = conn.query(c[:query]) + value = res.fetch_row + value = value.first.to_i + + if value >= c[:crit] + puts sprintf(message, value, c[:crit], value) + exit(EXIT_CRITICAL) + end + + if value >= c[:warn] + puts sprintf(message, value, c[:warn], value) + exit(EXIT_WARNING) + end + +else + puts "Please provide a warning and critical threshold" +end + +# if warning nor critical trigger, say OK and return performance data + +puts sprintf("Query '#{c[:query]}' result %d OK|#{perfdata}", value, value) \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/file_queues.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/file_queues.rb new file mode 100644 index 00000000000..72609503c6e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/file_queues.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby +# +# Nagios check +# Check 'work queue' directories by file count or age of oldest file +# + +require 'rubygems' +require 'choice' + +EXIT_OK = 0 +EXIT_WARNING = 1 +EXIT_CRITICAL = 2 +EXIT_UNKNOWN = 3 + +Choice.options do + header '' + header 'Specific options:' + + option :warn do + short '-w' + long '--warning=VALUE' + desc 'Warning threshold' + cast Integer + end + + option :crit do + short '-c' + long '--critical=VALUE' + desc 'Critical threshold' + cast Integer + end + + option :path do + short '-p' + long '--path=VALUE' + desc 'Path to directory' + end + + option :type do + short '-t' + long '--type=VALUE' + desc 'Either "age" (threshold of oldest file age in seconds) or "count" (number of files in direcrory)' + valid %w(age count) + end + +end + +c = Choice.choices + +# nagios performance data format: 'label'=value[UOM];[warn];[crit];[min];[max] +# see http://nagiosplug.sourceforge.net/developer-guidelines.html#AEN203 + + +if c[:warn] && c[:crit] + + if c[:type] == 'count' + perfdata = "file_count=%d;#{c[:warn]};#{c[:crit]}" + message = "File count %d for #{c[:path]} exceeds %d|#{perfdata}" + ok_message = "File count for %d for '#{c[:path]}' OK|#{perfdata}" + value = Dir.open(c[:path]).entries.size + else + perfdata = "oldest_file_age=%ds;#{c[:warn]};#{c[:crit]}" + message = "Oldest file age (%d seconds) in #{c[:path]} exceeds %d|#{perfdata}" + ok_message = "Oldest file age (%d seconds) for #{c[:path]} OK|#{perfdata}" + value = Dir.open(c[:path]).entries.collect {|f| Time.now - File.stat(File.join(c[:path], f)).mtime }.sort.last + end + + if value >= c[:crit] + puts sprintf(message, value, c[:crit], value) + exit(EXIT_CRITICAL) + end + + if value >= c[:warn] + puts sprintf(message, value, c[:warn], value) + exit(EXIT_WARNING) + end + +else + puts "Please provide a warning and critical threshold" +end + +# if warning nor critical trigger, say OK and return performance data + +puts sprintf(ok_message, value, value) \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/nagios_helper.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/nagios_helper.rb new file mode 100644 index 00000000000..46003ab2a23 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/files/default/plugins/nagios_helper.rb @@ -0,0 +1,26 @@ +require 'rubygems' +require 'choice' + +module NagiosHelper + EXIT_OK = 0 + EXIT_WARNING = 1 + EXIT_CRITICAL = 2 + EXIT_UNKNOWN = 3 + + def warning(text) + puts "WARNING: #{text}" + exit EXIT_WARNING + end + + def critical(text) + puts "CRITICAL: #{text}" + exit EXIT_CRITICAL + end + + def unknown(text) + puts "ERROR #{text}" + exit EXIT_UNKNOWN + end + +end + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/libraries/default.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/libraries/default.rb new file mode 100644 index 00000000000..ab351a67e93 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/libraries/default.rb @@ -0,0 +1,60 @@ +# +# Author:: Joshua Sierles +# Cookbook Name:: nagios +# Library:: default +# +# Copyright 2009, 37signals +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +def nagios_boolean(true_or_false) + true_or_false ? "1" : "0" +end + +def nagios_interval(seconds) + if seconds.to_i < node[:nagios][:interval_length].to_i + raise ArgumentError, "Specified nagios interval of #{seconds} seconds must be equal to or greater than the default interval length of #{node[:nagios][:interval_length]}" + end + interval = seconds / node[:nagios][:interval_length] + interval +end + +def nagios_attr(name) + node[:nagios][name] +end + +class Nagios +class Evaluator + + def initialize(node) + @b = binding + end + + def eval_with_context(str) + eval(str,@b) + end + + def log_eval_vars() + eval("Chef::Log.info('locals:'+local_variables.join(':') + '\nglobals:'+global_variables.join(':'))") + end + + def self.get_value_by_type(node, type) + location = node[:nagios][type] + e = Evaluator.new(node) + val = e.eval_with_context(location) + Chef::Log.info("Looking at #{location} for #{type}. Got: #{val}") + val + end + +end +end diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/metadata.json b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/metadata.json new file mode 100644 index 00000000000..1a7108bbbc0 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/metadata.json @@ -0,0 +1,48 @@ +{ + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "suggestions": { + + }, + "license": "Apache 2.0", + "conflicting": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls and configures Nagios 3 for a server and for clients using Chef 0.8 search capabilities.\n\n= REQUIREMENTS:\n\nRequires Chef 0.8+ for search capability of roles and data bags.\n\nA data bag named 'users' should exist, see \"DATA BAG\" below.\n\nThe monitoring server that uses this recipe should have a role named 'monitoring' or similar. A \"per-environment\" role should be created as well. See \"ROLES\" below.\n\nBecause of the heavy use of search, this recipe will not work with Chef Solo, as it cannot do any searches without a server.\n\n== Platform:\n\nTested on Ubuntu 9.04+ and Debian 5+.\n\n== Cookbooks:\n\n* apache2\n\n= ATTRIBUTES:\n\nAttributes under the 'nagios' namespace.\n\n== Client:\n\nThe following attributes are used for the client NRPE checks for warning and critical levels.\n\nchecks.memory.critical\nchecks.memory.warning\nchecks.load.critical\nchecks.load.warning\nchecks.smtp_host - default relayhost to check for connectivity. Default is an empty string, set via an attribute in a role.\nserver_role - the role that the nagios server will have in its run list that the clients can search for.\n\n== Server:\n\ndir - base server configuration directory.\nlog_dir - where the server logs.\ncache_dir - cached directory.\ndocroot - DocumentRoot for webui.\nconfig_subdir - for dropping in configurations as needed.\nnotifications_enabled - set to 1 to enable notification.\ncheck_external_commands\ndefault_contact_groups\nsysadmin_email - default notification email.\nsysadmin_sms_email - default notification sms.\nserver_auth_method - authentication with the server can be done with openid (using apache2::mod_auth_openid), or htauth (basic). The default is openid, any other value will use htauth (basic).\ntemplates\ninterval_length - minimum interval.\ndefault_host.check_interval\ndefault_host.retry_interval\ndefault_host.max_check_attempts\ndefault_host.notification_interval\ndefault_service.check_interval\ndefault_service.retry_interval\ndefault_service.max_check_attempts\ndefault_service.notification_interval\n\n= DATA BAGS:\n\nCreate a `users` data bag that will contain the users that will be able to log into the Nagios webui. Each user can use htauth with a specified password, or an openid. Users that should be able to log in should be in the sysadmin group. Example user data bag item:\n\n {\n \"id\": \"nagiosadmin\",\n \"groups\": \"sysadmin\",\n \"htpasswd\": \"hashed_htpassword\",\n \"openid\": \"http://nagiosadmin.myopenid.com/\",\n \"nagios\": {\n \"pager\": \"nagiosadmin_pager@example.com\",\n \"email\": \"nagiosadmin@example.com\"\n }\n }\n\nWhen using server_auth_method 'openid', use the openid in the data bag item. Any other value for this attribute (e.g., \"htauth\", \"htpasswd\", etc) will use the htpasswd value as the password in `/etc/nagios3/htpasswd.users`.\n\nThe openid must have the http:// and trailing /. The htpasswd must be the hashed value. Get this value with:\n\n % htpasswd -n nagiosadmin\n New password:\n Re-type new password:\n nagiosadmin:mbfzZrHsUUjds\n\nFor example use the \"mbfzZrHsUUjds\" value in the data bag.\n\n= ROLES:\n\nCreate a role to use for the monitoring server. The role name should match the value of the attribute \"nagios[:server_role]\". By default, this is 'monitoring'. For example:\n\n % cat roles/monitoring.rb\n name \"monitoring\"\n description \"Monitoring server\"\n run_list(\n \"recipe[nagios::server]\"\n )\n\n default_attributes(\n \"nagios\" => {\n \"server_auth_method\" => \"htauth\"\n }\n )\n\nAlso create per-environment role. For example, production nodes:\n\n % cat roles/production.rb\n name \"production\"\n description \"Nodes in the production environment.\"\n default_attributes(\n \"app_environment\" => \"production\"\n )\n\nMake sure to apply the production role to all nodes that should be monitored by the production monitoring server.\n\n= USAGE:\n\nFor a Nagios server, create a role named 'monitoring', and add the following recipe to the run_list:\n\n recipe[nagios::server]\n\nThis will allow client nodes to search for the server by this role and add its IP address to the allowed list for NRPE.\n\nTo install Nagios and NRPE on a client node:\n\n include_recipe \"nagios::client\"\n\nThis is a fairly complicated cookbook. We'll describe the components in detail.\n\n== Definitions:\n\nnagios_conf:: This definition is used to drop in a configuration file in the base Nagios configuration directory's conf.d. This can be used for customized configurations for various services.\n\n== Libraries:\n\ndefault:: The library included with the cookbook provides some helper methods used in templates.\n\n* nagios_boolean\n* nagios_interval - calculates interval based on interval length and a given number of seconds.\n* nagios_attr - retrieves a nagios attribute from the node.\n\n== Recipes:\n\n=== Client\n\nThe client recipe searches for allowed servers via a role named 'monitoring'. The recipe will also install the required packages and start the NRPE service. A custom plugin for checking memory is also added.\n\nClient commands for NRPE can be modified by editing the nrpe.cfg.erb template.\n\n=== Server\n\nThe server recipe sets up Apache as the web front end. The nagios::client recipe is also included. This recipe also does a number of searches to dynamically build the hostgroups to monitor, hosts that belong to them and admins to notify of events/alerts.\n\nThe recipe does the following:\n\n1. Searches for members of the sysadmins group by searching through 'users' data bag and adds them to a list for notification/contacts.\n2. Search all nodes for a role matching the app_environment.\n3. Search all available roles and build a list which will be the Nagios hostgroups.\n4. Search for all nodes of each role and add the hostnames to the hostgroups.\n5. Installs various packages required for the server.\n6. Sets up some configuration directories.\n7. Moves the package-installed Nagios configuration to a 'dist' directory.\n8. Disables the 000-default site (present on Debian/Ubuntu Apache2 package installations).\n9. Enables the Nagios web front end configuration.\n10. Sets up the configuration templates for services, contacts, hostgroups and hosts.\n\n*NOTE*: You will probably need to change the services.cfg.erb template for your environment.\n\nTo add custom commands for service checks, these can be done on a per-role basis by editing the 'services.cfg.erb' template. This template has some pre-configured checks that use role names used in an example infrastructure. Here's a brief description:\n\n* monitoring - check_smtp (e.g., postfix relayhost) w/ NRPE and tcp port 514 (e.g., rsyslog)\n* load_balancer - check_nginx with NRPE.\n* appserver - check_unicorn with NRPE, e.g. a Rails application using Unicorn.\n* database_master - check_mysql_server with NRPE for a MySQL database master.\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Sierles \nAuthor:: Nathan Haneysmith \nAuthor:: Joshua Timberman \n\nCopyright 2009, 37signals\nCopyright 2009-2010, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "providing": { + + }, + "description": "Installs and configures nagios", + "version": "0.4.4", + "maintainer": "Opscode, Inc.", + "replacing": { + + }, + "attributes": { + + }, + "maintainer_email": "cookbooks@opscode.com", + "name": "nagios", + "recipes": { + "nagios": "Includes the client recipe.", + "nagios::client": "Installs and configures a nagios client with nrpe", + "nagios::server": "Installs and configures a nagios server" + }, + "groupings": { + + }, + "dependencies": { + "apache2": [ + + ] + }, + "recommendations": { + + } +} \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/metadata.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/metadata.rb new file mode 100644 index 00000000000..15a0e1816bb --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/metadata.rb @@ -0,0 +1,16 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures nagios" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.4.4" + +recipe "nagios", "Includes the client recipe." +recipe "nagios::client", "Installs and configures a nagios client with nrpe" +recipe "nagios::server", "Installs and configures a nagios server" + +%w{ debian ubuntu }.each do |os| + supports os +end + +depends "apache2" diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/client.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/client.rb new file mode 100644 index 00000000000..83c53209fed --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/client.rb @@ -0,0 +1,107 @@ +# +# Author:: Joshua Sierles +# Author:: Joshua Timberman +# Author:: Nathan Haneysmith +# Cookbook Name:: nagios +# Recipe:: client +# +# Copyright 2009, 37signals +# Copyright 2009-2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Base filter for our environment +env_filter = " AND environment:#{node[:nagios][:config][:environment]}" + +# Find the provisioner ip # Assume provisioner is monitored by my nagios server +nodes = search(:node, "roles:provisioner-server#{env_filter}") + +# Get a list of provisioner addresses +prov_addresses = nodes.map { |n| Nagios::Evaluator.get_value_by_type(n, :admin_ip_eval) } + +# The master admin node is the first one in the list +provisioner_ip = prov_addresses[0] +admin_interface = Nagios::Evaluator.get_value_by_type(node, :admin_interface_eval) + +# Get the DNS domain name (i.e. pod.cloud.openstack.org) +domain_name = node[:dns][:domain] + +# Create a list of monitoring hosts (filter duplicate ip's in the list) +dup_list = Hash.new +mon_host = Array.new +search(:node, "role:nagios-server#{env_filter}") do |n| + ip = Nagios::Evaluator.get_value_by_type(n, :admin_ip_eval) + next if ip.nil? + if (!dup_list.has_key?(ip)) + mon_host << ip + dup_list[ip] = ip + end +end + +# Package/plugin install list +%w{ + nagios-nrpe-server + nagios-plugins + nagios-plugins-basic + nagios-plugins-standard + libjson-perl + libmath-calc-units-perl + libnagios-plugin-perl + libnagios-object-perl + libparams-validate-perl +}.each do |pkg| + package pkg +end + +# Service startup definition +service "nagios-nrpe-server" do + action :enable + supports :restart => true, :reload => true +end + +# Set directory ownership and permissions +remote_directory "/usr/lib/nagios/plugins" do + source "plugins" + owner "nagios" + group "nagios" + mode 0755 + files_mode 0755 +end + +# NTP server setup +ntp_servers = node[:ntp][:ntp_servers] unless node[:ntp].nil? or node[:ntp][:ntp_servers].nil? or node[:ntp][:ntp_servers].empty? +ntp_servers = "127.0.0.1" if node[:ntp].nil? or node[:ntp][:ntp_servers].nil? or node[:ntp][:ntp_servers].empty? + +#### setup variables for the different components +# common +vars = { :mon_host => mon_host, :provisioner_ip => provisioner_ip, :domain_name => domain_name, :admin_interface => admin_interface} +# ntp +vars.merge!({:ntp_servers => ntp_servers}) + +template "/etc/nagios/nrpe.cfg" do + source "nrpe.cfg.erb" + owner "nagios" + group "nagios" + mode "0644" + variables(vars) + notifies :restart, resources(:service => "nagios-nrpe-server") +end + +# Set file ownership and permissions +file "/usr/lib/nagios/plugins/check_dhcp" do + mode "4755" + owner "root" + group "root" +end + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/default.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/default.rb new file mode 100644 index 00000000000..ceb1a4ac29f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/default.rb @@ -0,0 +1,23 @@ +# +# Author:: Joshua Timberman +# Author:: Joshua Sierles +# Cookbook Name:: nagios +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc +# Copyright 2009, 37signals +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "nagios::client" diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/server.rb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/server.rb new file mode 100644 index 00000000000..f2f35f72286 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/recipes/server.rb @@ -0,0 +1,201 @@ +# +# Author:: Joshua Sierles +# Author:: Joshua Timberman +# Author:: Nathan Haneysmith +# Cookbook Name:: nagios +# Recipe:: server +# +# Copyright 2009, 37signals +# Copyright 2009-2010, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "apache2" +include_recipe "apache2::mod_ssl" +include_recipe "apache2::mod_rewrite" +include_recipe "nagios::client" + +# Begin recipe transactions +Chef::Log.debug("BEGIN nagios-server") + +# Get the config environment filter +env_filter = " AND nagios_config_environment:#{node[:nagios][:config][:environment]}" +Chef::Log.debug("env_filter [" + env_filter.to_s + "]") + +# Get the list of nodes +nodes = search(:node, "roles:nagios-client#{env_filter}") +Chef::Log.debug("Node query [" + nodes.to_s + "]") + +# Deal with an empty node list +if nodes.empty? + Chef::Log.debug("No nodes returned from search, using this node so hosts.cfg has data") + nodes = Array.new + nodes << node + Chef::Log.debug("nodes from hosts.cfg [" + nodes.to_s + "]") +else + # Make sure the server is in the list as well. + nodes << node unless nodes.include?(node) +end + +# Remove nodes that are in the process of going away. +nodes.delete_if { |n| n[:state] == "delete" } + +# Get a list of system administration users +sysadmins = search(:users, 'groups:sysadmin') +members = Array.new +sysadmins.each do |s| + Chef::Log.debug("Add system admin user [" + s['id'] + "]") + members << s['id'] +end + +# Make sure that the nodes have a field "ipaddress" that is the admin address +hosts = {} +nodes.each do |n| + ip = Nagios::Evaluator.get_value_by_type(n, :admin_ip_eval) + hosts[ip] = n unless ip.nil? +end + +# Build a hash of service name to the server fulfilling that role (NOT a list ) +role_list = Array.new +service_hosts = Hash.new +search(:role, "*:*") do |r| + role_list << r.name + search(:node, "role:#{r.name} #{env_filter}") do |n| + service_hosts[r.name] = n['hostname'] + end +end + +# Get the public domain name +if node[:public_domain] + public_domain = node[:public_domain] +else + public_domain = node[:domain] +end +Chef::Log.debug("Public domain [" + public_domain + "]") + +# Package install list +%w{ nagios3 nagios-nrpe-plugin nagios-images }.each do |pkg| + package pkg +end + +service "nagios3" do + supports :status => true, :restart => true, :reload => true + action [ :enable ] +end + +nagios_conf "nagios" do + config_subdir false +end + +# Set directory permissions +directory "#{node[:nagios][:dir]}/dist" do + owner "nagios" + group "nagios" + mode "0755" +end + +directory node[:nagios][:state_dir] do + owner "nagios" + group "nagios" + mode "0751" +end + +directory "#{node[:nagios][:state_dir]}/rw" do + owner "nagios" + group node[:apache][:user] + mode "2710" +end + +execute "archive default nagios object definitions" do + command "mv #{node[:nagios][:dir]}/conf.d/*_nagios*.cfg #{node[:nagios][:dir]}/dist" + not_if { Dir.glob(node[:nagios][:dir] + "/conf.d/*_nagios*.cfg").empty? } +end + +file "#{node[:apache][:dir]}/conf.d/nagios3.conf" do + action :delete +end + +case node[:nagios][:server_auth_method] +when "openid" + include_recipe "apache2::mod_auth_openid" +else + template "#{node[:nagios][:dir]}/htpasswd.users" do + source "htpasswd.users.erb" + owner "nagios" + group node[:apache][:user] + mode 0640 + variables( + :sysadmins => sysadmins + ) + end +end + +apache_site "000-default" do + enable false +end + +template "#{node[:apache][:dir]}/sites-available/nagios3.conf" do + source "apache2.conf.erb" + mode 0644 + variables :public_domain => public_domain + if ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/nagios3.conf") + notifies :reload, resources(:service => "apache2") + end +end + +apache_site "nagios3.conf" + +%w{ nagios cgi }.each do |conf| + nagios_conf conf do + config_subdir false + end +end + +# +# check_nova_ldap - one day if needed. +# +nova_commands = %w{ check_nova_api check_nova_compute check_nova_network check_nova_objectstore check_nova_rabbit check_nova_scheduler check_nova_volume check_nova_manage } + +swift_svcs = %w{swift-object swift-object-auditor swift-object-replicator swift-object-updater} +swift_svcs =swift_svcs + %w{swift-container swift-container-auditor swift-container-replicator swift-container-updater} +swift_svcs =swift_svcs + %w{swift-account swift-account-reaper swift-account-auditor swift-account-replicator} +swift_svcs =swift_svcs + ["swift-proxy"] + +glance_svcs = %w{glance-api glance-registry} + + +%w{ commands templates timeperiods}.each do |conf| + nagios_conf conf do + variables :nova_commands => nova_commands, :svcs => swift_svcs + glance_svcs + end +end + +nagios_conf "services" do + variables :service_hosts => service_hosts +end + +nagios_conf "contacts" do + variables :admins => sysadmins, :members => members +end + +nagios_conf "hostgroups" do + variables :roles => role_list +end + +nagios_conf "hosts" do + variables :hosts => hosts +end + +# End of recipe transactions +Chef::Log.debug("END nagios-server") + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/apache2.conf.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/apache2.conf.erb new file mode 100644 index 00000000000..319fd689408 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/apache2.conf.erb @@ -0,0 +1,35 @@ + + ServerAdmin <%= node[:nagios][:sysadmin_email] %> + ServerName <%= node[:fqdn] %> + ServerAlias nagios nagios.<%= node[:app_environment]%>.<%= @public_domain %> + DocumentRoot <%= node[:nagios][:docroot] %> + CustomLog /var/log/nagios3/apache_access.log combined + ErrorLog /var/log/nagios3/apache_error.log + + ScriptAlias /cgi-bin/nagios3 /usr/lib/cgi-bin/nagios3 + ScriptAlias /nagios3/cgi-bin /usr/lib/cgi-bin/nagios3 + + Alias /nagios3/stylesheets /etc/nagios3/stylesheets + Alias /nagios3 <%= node[:nagios][:docroot] %> + +<% case node[:nagios][:server_auth_method] -%> +<% when "openid" -%> + + AuthName "Nagios Server" + AuthOpenIDEnabled On + AuthOpenIDDBLocation /var/cache/apache2/mod_auth_openid.db + AuthOpenIDUserProgram /usr/local/bin/mod_auth_openid.rb + +<% else -%> + + AuthName "Nagios Server" + AuthType Basic + AuthUserFile "<%= node[:nagios][:dir] %>/htpasswd.users" + require valid-user + +<% end -%> + + RewriteEngine On + RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.*index\.html\ HTTP/ + RewriteRule ^(.*)index\.html$ $1 [R=301,L] + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/cgi.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/cgi.cfg.erb new file mode 100644 index 00000000000..3a27490a29a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/cgi.cfg.erb @@ -0,0 +1,231 @@ +# +# Autogenerated by Chef. +# +# MAIN CONFIGURATION FILE +# This tells the CGIs where to find your main configuration file. +# The CGIs will read the main and host config files for any other +# data they might need. + +main_config_file=<%= node[:nagios][:dir] %>/nagios.cfg + +# PHYSICAL HTML PATH +# This is the path where the HTML files for Nagios reside. This +# value is used to locate the logo images needed by the statusmap +# and statuswrl CGIs. + +physical_html_path=<%= node[:nagios][:docroot] %> + +# URL HTML PATH +# This is the path portion of the URL that corresponds to the +# physical location of the Nagios HTML files (as defined above). +# This value is used by the CGIs to locate the online documentation +# and graphics. If you access the Nagios pages with an URL like +# http://www.myhost.com/nagios, this value should be '/nagios' +# (without the quotes). + +url_html_path=/nagios3 + +# CONTEXT-SENSITIVE HELP +# This option determines whether or not a context-sensitive +# help icon will be displayed for most of the CGIs. +# Values: 0 = disables context-sensitive help +# 1 = enables context-sensitive help + +show_context_help=1 + +# NAGIOS PROCESS CHECK COMMAND +# This is the full path and filename of the program used to check +# the status of the Nagios process. It is used only by the CGIs +# and is completely optional. However, if you don't use it, you'll +# see warning messages in the CGIs about the Nagios process +# not running and you won't be able to execute any commands from +# the web interface. The program should follow the same rules +# as plugins; the return codes are the same as for the plugins, +# it should have timeout protection, it should output something +# to STDIO, etc. +# +# Note: The command line for the check_nagios plugin below may +# have to be tweaked a bit, as different versions of the plugin +# use different command line arguments/syntaxes. + +nagios_check_command=/usr/lib/nagios/plugins/check_nagios <%= node[:nagios][:cache_dir] %>/status.dat 5 '/usr/sbin/nagios3' + +# AUTHENTICATION USAGE +# This option controls whether or not the CGIs will use any +# authentication when displaying host and service information, as +# well as committing commands to Nagios for processing. +# +# Read the HTML documentation to learn how the authorization works! +# +# NOTE: It is a really *bad* idea to disable authorization, unless +# you plan on removing the command CGI (cmd.cgi)! Failure to do +# so will leave you wide open to kiddies messing with Nagios and +# possibly hitting you with a denial of service attack by filling up +# your drive by continuously writing to your command file! +# +# Setting this value to 0 will cause the CGIs to *not* use +# authentication (bad idea), while any other value will make them +# use the authentication functions (the default). + +use_authentication=1 + +# DEFAULT USER +# Setting this variable will define a default user name that can +# access pages without authentication. This allows people within a +# secure domain (i.e., behind a firewall) to see the current status +# without authenticating. You may want to use this to avoid basic +# authentication if you are not using a secure server since basic +# authentication transmits passwords in the clear. +# +# Important: Do not define a default username unless you are +# running a secure web server and are sure that everyone who has +# access to the CGIs has been authenticated in some manner! If you +# define this variable, anyone who has not authenticated to the web +# server will inherit all rights you assign to this user! + +#default_user_name=guest + +# SYSTEM/PROCESS INFORMATION ACCESS +# This option is a comma-delimited list of all usernames that +# have access to viewing the Nagios process information as +# provided by the Extended Information CGI (extinfo.cgi). By +# default, *no one* has access to this unless you choose to +# not use authorization. You may use an asterisk (*) to +# authorize any user who has authenticated to the web server. + +authorized_for_system_information=* + +# CONFIGURATION INFORMATION ACCESS +# This option is a comma-delimited list of all usernames that +# can view ALL configuration information (hosts, commands, etc). +# By default, users can only view configuration information +# for the hosts and services they are contacts for. You may use +# an asterisk (*) to authorize any user who has authenticated +# to the web server. + +authorized_for_configuration_information=* + +# SYSTEM/PROCESS COMMAND ACCESS +# This option is a comma-delimited list of all usernames that +# can issue shutdown and restart commands to Nagios via the +# command CGI (cmd.cgi). Users in this list can also change +# the program mode to active or standby. By default, *no one* +# has access to this unless you choose to not use authorization. +# You may use an asterisk (*) to authorize any user who has +# authenticated to the web server. + +authorized_for_system_commands=* + +# GLOBAL HOST/SERVICE VIEW ACCESS +# These two options are comma-delimited lists of all usernames that +# can view information for all hosts and services that are being +# monitored. By default, users can only view information +# for hosts or services that they are contacts for (unless you +# you choose to not use authorization). You may use an asterisk (*) +# to authorize any user who has authenticated to the web server. + + +authorized_for_all_services=* +authorized_for_all_hosts=* + +# GLOBAL HOST/SERVICE COMMAND ACCESS +# These two options are comma-delimited lists of all usernames that +# can issue host or service related commands via the command +# CGI (cmd.cgi) for all hosts and services that are being monitored. +# By default, users can only issue commands for hosts or services +# that they are contacts for (unless you you choose to not use +# authorization). You may use an asterisk (*) to authorize any +# user who has authenticated to the web server. + +authorized_for_all_service_commands=* +authorized_for_all_host_commands=* + +# STATUSMAP BACKGROUND IMAGE +# This option allows you to specify an image to be used as a +# background in the statusmap CGI. It is assumed that the image +# resides in the HTML images path (i.e. /usr/local/nagios/share/images). +# This path is automatically determined by appending "/images" +# to the path specified by the 'physical_html_path' directive. +# Note: The image file may be in GIF, PNG, JPEG, or GD2 format. +# However, I recommend that you convert your image to GD2 format +# (uncompressed), as this will cause less CPU load when the CGI +# generates the image. + +#statusmap_background_image=smbackground.gd2 + +# DEFAULT STATUSMAP LAYOUT METHOD +# This option allows you to specify the default layout method +# the statusmap CGI should use for drawing hosts. If you do +# not use this option, the default is to use user-defined +# coordinates. Valid options are as follows: +# 0 = User-defined coordinates +# 1 = Depth layers +# 2 = Collapsed tree +# 3 = Balanced tree +# 4 = Circular +# 5 = Circular (Marked Up) + +default_statusmap_layout=5 + +# DEFAULT STATUSWRL LAYOUT METHOD +# This option allows you to specify the default layout method +# the statuswrl (VRML) CGI should use for drawing hosts. If you +# do not use this option, the default is to use user-defined +# coordinates. Valid options are as follows: +# 0 = User-defined coordinates +# 2 = Collapsed tree +# 3 = Balanced tree +# 4 = Circular + +default_statuswrl_layout=4 + +# STATUSWRL INCLUDE +# This option allows you to include your own objects in the +# generated VRML world. It is assumed that the file +# resides in the HTML path (i.e. /usr/local/nagios/share). + +#statuswrl_include=myworld.wrl + +# PING SYNTAX +# This option determines what syntax should be used when +# attempting to ping a host from the WAP interface (using +# the statuswml CGI. You must include the full path to +# the ping binary, along with all required options. The +# $HOSTADDRESS$ macro is substituted with the address of +# the host before the command is executed. +# Please note that the syntax for the ping binary is +# notorious for being different on virtually ever *NIX +# OS and distribution, so you may have to tweak this to +# work on your system. + +ping_syntax=/bin/ping -n -U -c 5 $HOSTADDRESS$ + +# REFRESH RATE +# This option allows you to specify the refresh rate in seconds +# of various CGIs (status, statusmap, extinfo, and outages). + +refresh_rate=90 + +# SOUND OPTIONS +# These options allow you to specify an optional audio file +# that should be played in your browser window when there are +# problems on the network. The audio files are used only in +# the status CGI. Only the sound for the most critical problem +# will be played. Order of importance (higher to lower) is as +# follows: unreachable hosts, down hosts, critical services, +# warning services, and unknown services. If there are no +# visible problems, the sound file optionally specified by +# 'normal_sound' variable will be played. +# +# +# = +# +# Note: All audio files must be placed in the /media subdirectory +# under the HTML path (i.e. /usr/local/nagios/share/media/). + +#host_unreachable_sound=hostdown.wav +#host_down_sound=hostdown.wav +#service_critical_sound=critical.wav +#service_warning_sound=warning.wav +#service_unknown_sound=warning.wav +#normal_sound=noproblem.wav diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/commands.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/commands.cfg.erb new file mode 100644 index 00000000000..edfa2f3c638 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/commands.cfg.erb @@ -0,0 +1,402 @@ +# +# Automatically generated by Chef + +# Host checks +define command { + command_name check-host-alive + command_line $USER1$/check_ping -H $HOSTADDRESS$ -w 2000,80% -c 3000,100% -p 1 +} + +# Service checks +define command { + command_name check_ec2_count + command_line $USER1$/count_ec2_instances.sh +} + +define command { + command_name check_solr_proc + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_solr_proc -t 20 +} + +define command { + command_name check_stompserver + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_stompserver -t 20 +} + +define command { + command_name check_chef_client + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_chef_client -t 20 +} + +define command { + command_name check_collectd_client + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_collectd_client -t 20 +} + +define command { + command_name check_rabbitmq + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_rabbitmq -t 20 +} + +define command { + command_name check_authz + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_authz -t 20 +} + +define command { + command_name check_audit + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_audit -t 20 +} + +define command { + command_name check-nagios + command_line $USER1$/check_nagios -F <%= node[:nagios][:cache_dir] %>/status.dat -e 4 -C /usr/sbin/nagios3 +} + +define command { + command_name check_file_age + command_line $USER1$/check_by_ssh -H $HOSTADDRESS$ -C "/usr/local/bin/check_file -i -p $ARG1$ -w $ARG2$ -c $ARG3$" -l root +} + +define command { + command_name check_file_count + command_line $USER1$/check_by_ssh -H $HOSTADDRESS$ -C "/usr/lib/nagios/checks/file_queues.rb -t count -p $ARG1$ -w $ARG2$ -c $ARG3$" -l app +} + +define command { + command_name check_log + command_line $USER1$/check_by_ssh -H $HOSTADDRESS$ -C "/usr/lib64/nagios/plugins/check_log -F $ARG1$ -O /tmp/nagios.logcheck.processed -q $ARG2$" -l root +} + +define command { + command_name check_solr + command_line $USER1$/check_solr.rb -c 10 -h $HOSTADDRESS$ -r 10 -p $ARG1$ + +} + +define command { + command_name query_solr + command_line $USER1$/check_solr.rb -c 10 -h $HOSTADDRESS$ -r 10 -p $ARG1$ -q $ARG2$ + +} + +define command { + command_name check_mem + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_mem -t 20 +} + + +define command { + command_name check_query_count + command_line $USER1$/db_queues.rb -H $HOSTADDRESS$ -u $ARG1$ -p $ARG2$ -d $ARG3$ -q $ARG4$ -w $ARG5$ -c $ARG6$ +} + +define command { + command_name check_local_disk + command_line $USER1$/check_disk -w $ARG1$ -c $ARG2$ -p $ARG3$ +} + +define command { + command_name check_local_load + command_line $USER1$/check_load -w $ARG1$ -c $ARG2$ +} + +define command { + command_name check_local_procs + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_total_procs -t 20 +} + +define command { + command_name check_zombie_procs + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_zombie_procs -t 20 +} + +define command { + command_name check_local_users + command_line $USER1$/check_users -w $ARG1$ -c $ARG2$ +} + +define command { + command_name check_local_log + command_line $USER1$/check_log -F $ARG1$ -O /tmp/nagios.logcheck.processed -q $ARG2$ +} + +define command { + command_name check_dns + command_line $USER1$/check_dns -H $ARG1$ -s $HOSTADDRESS$ +} + +define command { + command_name check_ftp + command_line $USER1$/check_ftp -H $HOSTADDRESS$ +} + +define command { + command_name check_hpjd + command_line $USER1$/check_hpjd -H $HOSTADDRESS$ -C public +} + +define command { + command_name check_ganglia_web + command_line $USER1$/check_http -f follow -a nagiosadmin:password -I $HOSTADDRESS$ -H $HOSTADDRESS$ -u "/ganglia" +} + +define command { + command_name check_http + command_line $USER1$/check_http -I $HOSTADDRESS$ -H $HOSTADDRESS$ +} + +define command { + command_name check_http_content + command_line $USER1$/check_http -I $HOSTADDRESS$ -H $ARG1$ -u $ARG2$ -s $ARG3$ +} + +define command { + command_name check_http_port + command_line $USER1$/check_http -I $HOSTADDRESS$ -H $HOSTADDRESS$ -p $ARG1$ +} + +define command { + command_name check_http_port_expect + command_line $USER1$/check_http -I $HOSTADDRESS$ -H $HOSTADDRESS$ -p $ARG1$ -e $ARG2$ +} + +define command { + command_name check_http_ip_port + command_line $USER1$/check_http -I $ARG1$ -H $ARG1$ -p $ARG2$ +} + +define command { + command_name check_mongrel_http_content + command_line $USER1$/check_http -I $HOSTADDRESS$ -H $ARG1$ -u $ARG2$ -s $ARG3$ -p $ARG4$ +} + +define command { + command_name check_nntp + command_line $USER1$/check_nntp -H $HOSTADDRESS$ +} + +define command { + command_name check_ping + command_line $USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -p 5 +} + +define command { + command_name check_pop + command_line $USER1$/check_pop -H $HOSTADDRESS$ +} + +define command { + command_name check_smtp + command_line $USER1$/check_smtp -H $HOSTADDRESS$ +} + +define command { + command_name check_tcp + command_line $USER1$/check_tcp -H $HOSTADDRESS$ -p $ARG1$ +} + +define command { + command_name check_ssh + command_line $USER1$/check_ssh $HOSTADDRESS$ +} + +define command { + command_name check_telnet + command_line $USER1$/check_tcp -H $HOSTADDRESS$ -p 23 +} + +define command { + command_name check_udp + command_line $USER1$/check_udp -H $HOSTADDRESS$ -p $ARG1$ +} + +# 'check_mysql' check mysql connectivity +define command { + command_name check_mysql + command_line $USER1$/check_mysql -H $ARG1$ -u nagios -p $ARG2$ +} + +define command { + command_name check_mysql_master + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -u $ARG1$ -p $ARG2$ +} + +# 'check_mysqlrep' command for mysql replication status +define command { + command_name check_replication + command_line $USER1$/check_nrpe -c check_replication -H $HOSTADDRESS$ -t 20 +} + +define command { + command_name check_mysqlrep + command_line $USER1$/check-mysql-slave.pl --host $HOSTADDRESS$ --port $ARG1$ --user replcheck --password $ARG2$ --warn $ARG3$ --crit $ARG4$ +} + +# nrpe remote host checks +define command { + command_name check_nrpe_alive + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -t 20 + } + +define command { + command_name check_nrpe + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c $ARG1$ -t 20 + } + +define command { + command_name check_load + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_load -t 20 +} + +define command { + command_name check_dynomite_proc + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_dynomite_proc -t 20 +} + +define command { + command_name check_couchdb_proc + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_couchdb_proc -t 20 +} + +define command { + command_name check_disk + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_$ARG1$ -t 20 +} + +define command { + command_name check_all_disks + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_all_disks -t 20 +} + +define command { + command_name check_traffic + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c traffic -t 20 +} + +define command { + command_name check_mysqlhealth + command_line $USER1$/check_mysqlhealth.pl -H $HOSTADDRESS$ -u nagios -p $ARG1$ --port 3306 --wa 500 --cc 550 +} + +define command { + command_name check_iowait_time + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_iowait_time -t 20 +} + +define command { + command_name check_tps + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_tps -t 20 +} + +define command { + command_name check_swap + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_swap -t 20 +} + +define command { + command_name check_solr_index + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_$ARG1$_solr_index -t 20 +} + +define command { + command_name check_gmond + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_gmond +} + +define command { + command_name check_gmetad + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_gmetad +} + +define command { + command_name check_local_time + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_local_time +} + +define command { + command_name check_ntp + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_ntp +} + +define command { + command_name check_apt + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_apt +} + +define command { + command_name remote_check_dns + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_dns +} + +define command { + command_name check_dhcp + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_dhcp +} + +# dummy commands for passive checks + +define command { + command_name no-disk-checks + command_line /usr/local/nagios/libexec/check_dummy 2 "CRITICAL: Results of disk checks not coming in!" +} + +define command { + command_name no-mem-checks + command_line /usr/local/nagios/libexec/check_dummy 2 "CRITICAL: Results of memory checks not coming in!" +} + +# Notification +define command { + command_name host-notify-by-email + command_line /usr/bin/printf "%b" "$LONGDATETIME$\n\n$HOSTALIAS$ $NOTIFICATIONTYPE$ $HOSTSTATE$\n\n$HOSTOUTPUT$\n\nLogin: ssh://$HOSTNAME$" | /bin/mail -s "$NOTIFICATIONTYPE$ - $HOSTALIAS$ $HOSTSTATE$!" $CONTACTEMAIL$ +} + +define command { + command_name host-notify-by-sms-email + command_line /usr/bin/printf "%b" "$HOSTALIAS$ $NOTIFICATIONTYPE$ $HOSTSTATE$\n\n$HOSTOUTPUT$" | /bin/mail -s "$HOSTALIAS$ $HOSTSTATE$!" $CONTACTPAGER$ +} + +# 'notify-by-email' command definition +define command { + command_name service-notify-by-email + command_line /usr/bin/printf "%b" "$LONGDATETIME$ - $SERVICEDESC$ $SERVICESTATE$\n\n$HOSTALIAS$ $NOTIFICATIONTYPE$\n\n$SERVICEOUTPUT$\n\nLogin: ssh://$HOSTNAME$" | /bin/mail -s "** $NOTIFICATIONTYPE$ - $HOSTALIAS$ - $SERVICEDESC$ - $SERVICESTATE$" $CONTACTEMAIL$ +} + +#define command { +# command_name process-service-perfdata +# command_line /usr/lib/nagios/plugins/process_perfdata.pl +#} + +#define command { +# command_name process-host-perfdata +# command_line /usr/lib/nagios/plugins/process_perfdata.pl -d HOSTPERFDATA +#} + +<% @nova_commands.each { |cmd| %> +define command { + command_name <%= cmd %> + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c <%= cmd %> +} +<% } %> + +define command { + command_name check_nova_mysql + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_mysql_server +} + +<% @svcs.each { |cmd| %> +define command { + command_name check_<%= cmd %> + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_<%= cmd %> +} +<% } unless @svcs.nil? %> + +<% %w{glance-api glance-registry swift-object swift-container swift-account swift-proxy}.each do |svc| %> +define command { + command_name check_port_<%=svc %> + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_port_<%=svc %> +} +<% end %> + + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/contacts.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/contacts.cfg.erb new file mode 100644 index 00000000000..844c1d4d823 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/contacts.cfg.erb @@ -0,0 +1,32 @@ +define contact { + contact_name root + alias Root + service_notification_period 24x7 + host_notification_period 24x7 + service_notification_options w,u,c,r + host_notification_options d,r + service_notification_commands service-notify-by-email + host_notification_commands host-notify-by-email + email root@localhost +} + +define contactgroup { + contactgroup_name admins + alias Nagios Administrators + members <%= @members.join(',') %> +} + +define contactgroup { + contactgroup_name admins-sms + alias Sysadmin SMS + members <%= @members.join(',') %> +} + +<% @admins.each do |a| -%> +define contact { + use default-contact + contact_name <%= a['id'] %> + email <%= a['nagios']['email'] %> +} + +<% end -%> diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/hostgroups.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/hostgroups.cfg.erb new file mode 100644 index 00000000000..41a3895379a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/hostgroups.cfg.erb @@ -0,0 +1,16 @@ +# Hostgroup definitions +# Automatically generated by Chef + +define hostgroup { + hostgroup_name all + alias all + members * +} + +<% @roles.each do |r| -%> +define hostgroup { + hostgroup_name <%= r %> + alias <%= r %> +} + +<% end -%> diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/hosts.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/hosts.cfg.erb new file mode 100644 index 00000000000..51da59b6358 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/hosts.cfg.erb @@ -0,0 +1,16 @@ +# Generated by Chef +# host definitions + +<% @hosts.each do |ip, n| -%> +define host { + use server + address <%= ip %> + host_name <%= n["hostname"] %> +<% if n.run_list.roles.nil? -%> + hostgroups all +<% else -%> + hostgroups <%= n.run_list.roles.to_a.join(",") %> +<% end -%> +} + +<% end -%> diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/htpasswd.users.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/htpasswd.users.erb new file mode 100644 index 00000000000..96ee5035608 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/htpasswd.users.erb @@ -0,0 +1,4 @@ +# Generated by Chef. +<% @sysadmins.each do |sa| -%> +<%= sa["id"] %>:<%= sa["htpasswd"] %> +<% end -%> diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/nagios.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/nagios.cfg.erb new file mode 100644 index 00000000000..dc7a0c3ce61 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/nagios.cfg.erb @@ -0,0 +1,156 @@ +# Generated by Chef +# + +log_file=<%= node[:nagios][:log_dir] %>/nagios.log + +admin_email=<%= node[:nagios][:sysadmin_email] %> +admin_pager=<%= node[:nagios][:sysadmin_sms_email] %> + +cfg_dir=<%= node[:nagios][:dir] %>/<%= node[:nagios][:config_subdir] %> + +object_cache_file=<%= node[:nagios][:cache_dir] %>/objects.cache +precached_object_file=<%= node[:nagios][:cache_dir] %>/objects.precache + +resource_file=/etc/nagios3/resource.cfg +status_file=<%= node[:nagios][:cache_dir] %>/status.dat +status_update_interval=10 +nagios_user=nagios +nagios_group=nagios +check_external_commands=<%= nagios_boolean(node[:nagios][:check_external_commands]) %> + +# NOTE: Setting this value to -1 causes Nagios to check the external command file as often as possible. +command_check_interval=-1 + +command_file=<%= node[:nagios][:state_dir] %>/rw/nagios.cmd +external_command_buffer_slots=4096 +lock_file=/var/run/nagios3/nagios3.pid +temp_file=<%= node[:nagios][:cache_dir] %>/nagios.tmp +temp_path=/tmp +event_broker_options=-1 + +log_rotation_method=d +log_archive_path=<%= node[:nagios][:log_dir] %>/archives +use_syslog=1 +log_notifications=1 +log_service_retries=1 +log_host_retries=1 +log_event_handlers=1 +log_initial_states=0 +log_external_commands=1 +log_passive_checks=1 + +service_inter_check_delay_method=s +max_service_check_spread=5 +service_interleave_factor=s +host_inter_check_delay_method=s +max_host_check_spread=5 +max_concurrent_checks=0 +check_result_reaper_frequency=10 +max_check_result_reaper_time=30 +check_result_path=<%= node[:nagios][:state_dir] %>/spool/checkresults +max_check_result_file_age=3600 +cached_host_check_horizon=15 +cached_service_check_horizon=15 +enable_predictive_host_dependency_checks=1 +enable_predictive_service_dependency_checks=1 +soft_state_dependencies=0 + +auto_reschedule_checks=0 +auto_rescheduling_interval=30 +auto_rescheduling_window=180 + +sleep_time=0.25 +service_check_timeout=60 +host_check_timeout=30 +event_handler_timeout=30 +notification_timeout=30 +ocsp_timeout=5 +perfdata_timeout=5 + +retain_state_information=1 +state_retention_file=<%= node[:nagios][:state_dir] %>/retention.dat +retention_update_interval=60 +use_retained_program_state=1 +use_retained_scheduling_info=1 +retained_host_attribute_mask=0 +retained_service_attribute_mask=0 +retained_process_host_attribute_mask=0 +retained_process_service_attribute_mask=0 +retained_contact_host_attribute_mask=0 +retained_contact_service_attribute_mask=0 + +interval_length=<%= node[:nagios][:interval_length] %> + +use_aggressive_host_checking=0 + +execute_service_checks=1 +accept_passive_service_checks=1 +execute_host_checks=1 +accept_passive_host_checks=1 + +enable_notifications=<%= node[:nagios][:notifications_enabled] %> +enable_event_handlers=1 + +#process_performance_data=1 +process_performance_data=0 + +#service_perfdata_command=ngraph-process-service-perfdata-pipe +#host_perfdata_command=process-host-perfdata +#service_perfdata_command=process-service-perfdata +#host_perfdata_file=/tmp/host-perfdata +#service_perfdata_file=/tmp/service-perfdata +#host_perfdata_file_template=[HOSTPERFDATA]\t$TIMET$\t$HOSTNAME$\t$HOSTEXECUTIONTIME$\t$HOSTOUTPUT$\t$HOSTPERFDATA$ +#service_perfdata_file_template=[SERVICEPERFDATA]\t$TIMET$\t$HOSTNAME$\t$SERVICEDESC$\t$SERVICEEXECUTIONTIME$\t$SERVICELATENCY$\t$SERVICEOUTPUT$\t$SERVICEPERFDATA$ +#host_perfdata_file_mode=a +#service_perfdata_file_mode=a +#host_perfdata_file_processing_interval=0 +#service_perfdata_file_processing_interval=0 +#host_perfdata_file_processing_command=process-host-perfdata-file +#service_perfdata_file_processing_command=process-service-perfdata-file + +obsess_over_services=0 +#ocsp_command=somecommand +obsess_over_hosts=0 +#ochp_command=somecommand +translate_passive_host_checks=0 +passive_host_checks_are_soft=0 +check_for_orphaned_services=1 +check_for_orphaned_hosts=1 +check_service_freshness=1 +service_freshness_check_interval=60 +check_host_freshness=0 +host_freshness_check_interval=60 +additional_freshness_latency=15 + +enable_flap_detection=1 +low_service_flap_threshold=5.0 +high_service_flap_threshold=20.0 +low_host_flap_threshold=5.0 +high_host_flap_threshold=20.0 + +date_format=iso8601 + +use_timezone=UTC +p1_file=/usr/lib/nagios3/p1.pl +enable_embedded_perl=1 +use_embedded_perl_implicitly=1 + +illegal_object_name_chars=`~!$%^&*|'"<>?,()= +illegal_macro_output_chars=`~$&|'"<> + +use_regexp_matching=0 +use_true_regexp_matching=0 + +daemon_dumps_core=0 + +use_large_installation_tweaks=0 + +enable_environment_macros=1 + +#free_child_process_memory=1 +#child_processes_fork_twice=1 + +debug_level=0 +debug_verbosity=1 +debug_file=<% node[:nagios][:state_dir] %>/nagios.debug +max_debug_file_size=1000000 diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/nrpe.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/nrpe.cfg.erb new file mode 100644 index 00000000000..48c9d2b9a50 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/nrpe.cfg.erb @@ -0,0 +1,47 @@ +pid_file=/var/run/nrpe.pid +server_port=5666 +nrpe_user=nagios +nrpe_group=nagios +dont_blame_nrpe=0 +debug=0 +command_timeout=60 + +include_dir=/etc/nagios/nrpe.d/ + +<% unless @mon_host.nil? or @mon_host.empty? -%> +allowed_hosts=<%= @mon_host.join(',') %> +<% end -%> + +command[check_users]=/usr/lib/nagios/plugins/check_users -w 20 -c 30 +command[check_load]=/usr/lib/nagios/plugins/check_load -w <%= node[:nagios][:checks][:load][:warning] %> -c <%= node[:nagios][:checks][:load][:critical] %> +command[check_all_disks]=/usr/lib/nagios/plugins/check_disk -w 8% -c 5% -A -x /dev/shm -X nfs -i /boot +command[check_zombie_procs]=/usr/lib/nagios/plugins/check_procs -w 5 -c 10 -s Z +command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 500 -c 800 +command[check_swap]=/usr/lib/nagios/plugins/check_swap -w '50%' -c '25%' +command[check_mem]=/usr/lib/nagios/plugins/check_mem.sh -w <%= node[:nagios][:checks][:memory][:warning] %> -c <%= node[:nagios][:checks][:memory][:critical] %> -p +command[check_chef_client]=/usr/lib/nagios/plugins/check_procs -w 1:2 -c 1:2 -C chef-client +command[check_smtp]=/usr/lib/nagios/plugins/check_smtp -H <%= node[:nagios][:checks][:smtp_host] %> +command[check_nginx]=/usr/lib/nagios/plugins/check_procs -w 2:3 -c 1:5 -C nginx +command[check_sphinx]=/usr/lib/nagios/plugins/check_procs -c 1:1 -C searchd +<% unless node[:mysql].nil? -%> +command[check_mysql_server]=/usr/lib/nagios/plugins/check_mysql -H localhost -u debian-sys-maint -p <%= node[:mysql][:server_debian_password] %> +<% end -%> +#command[check_unicorn]=/usr/lib/nagios/plugins/check_procs -c 4:8 -C unicorn_rails +# +# memcached checks require cpan modules, "Cache::Memcached" and "Nagios::Plugins::Memcached" +#command[check_memcached_response]=/usr/local/bin/check_memcached -H <%= node[:ipaddress] %> -w 3 -c 5 +#command[check_memcached_size]=/usr/local/bin/check_memcached -H <%= node[:ipaddress] %> --size-warning 60 --size-critical 80 +#command[check_memcached_hit]=/usr/local/bin/check_memcached -H <%= node[:ipaddress] %> --hit-warning 40 --hit-critical 20 + +command[check_gmond]=/usr/lib/nagios/plugins/check_procs -w 1:2 -c 1:2 -C gmond +command[check_gmetad]=/usr/lib/nagios/plugins/check_procs -w 1:2 -c 1:2 -C gmetad + +command[check_local_time]=/usr/lib/nagios/plugins/check_ntp_time -H 127.0.0.1 + +command[check_ntp]=/usr/lib/nagios/plugins/check_ntp -H <%= @ntp_servers %> + +command[check_dhcp]=/usr/lib/nagios/plugins/check_dhcp -s <%= @provisioner_ip %> -u -i <%= @admin_interface %> +command[check_dns]=/usr/lib/nagios/plugins/check_dns -H <%= @domain_name %> + +command[check_apt]=/usr/lib/nagios/plugins/check_apt + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/services.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/services.cfg.erb new file mode 100644 index 00000000000..67c028d042b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/services.cfg.erb @@ -0,0 +1,373 @@ +# Nagios Service Definitions. +# +# Automatically generated by Chef. + +define service { + use default-service + host_name * + service_description ping + check_command check_ping!200.0,20%!500.0,60% +} + +# monitoring service checks +<% unless @service_hosts['nagios-server'].nil? -%> +define service { + service_description Nagios + hostgroup_name nagios-server + check_command check-nagios + contact_groups admins + use default-service +} +<% end -%> + +# default checks for all hosts +define service { + service_description Chef Client + hostgroup_name all + check_command check_chef_client + use default-service +} + +define service { + service_description Free Space All Disks + hostgroup_name all + check_command check_all_disks + use default-service +} + +define service { + service_description Load Average + hostgroup_name all + check_command check_load + use default-service +} + +define service { + service_description Free Memory + hostgroup_name all + check_command check_mem + use default-service +} + +define service { + service_description SSH + hostgroup_name all + check_command check_ssh + use default-service +} + +define service { + service_description Local Processes + hostgroup_name all + check_command check_local_procs + use default-service +} + +define service { + service_description Zombie Processes + hostgroup_name all + check_command check_zombie_procs + use default-service +} + +<% unless @service_hosts['ganglia-client'].nil? and @service_hosts['ganglia-server'].nil? -%> +define service { + service_description Ganglia + hostgroup_name all + check_command check_gmond + use default-service +} +<% end -%> + +define service { + service_description Check local ntp daemon + hostgroup_name all + check_command check_local_time + use default-service +} + +define service { + service_description Check ntp server daemon + hostgroup_name all + check_command check_ntp + use default-service +} + +define service { + service_description Check SWAP level + hostgroup_name all + check_command check_swap + use default-service +} + +define service { + service_description Check APT update state + hostgroup_name all + check_command check_apt + use default-service +} + +define service { + service_description Check DNS Resolver + hostgroup_name all + check_command remote_check_dns + use default-service +} + +<% unless @service_hosts['ganglia-server'].nil? -%> +define service { + service_description GMetad + hostgroup_name ganglia-server + check_command check_gmetad + use default-service +} + +define service { + service_description Ganglia Web + hostgroup_name ganglia-server + check_command check_ganglia_web + use default-service +} +<% end -%> +<% unless @service_hosts['provisioner-server'].nil? -%> +# These are the services to watch on an admin node +define service { + service_description Crowbar Web Server + hostgroup_name provisioner-server + check_command check_tcp!3000 + use default-service +} +define service { + service_description Install Web Server + hostgroup_name provisioner-server + check_command check_http_port!8091 + use default-service +} +define service { + service_description DHCP Server + hostgroup_name provisioner-server + check_command check_dhcp + use default-service +} +<% end -%> + +<% unless @service_hosts['nova-single-machine'].nil? -%> +# These are the services to watch on a nova-single-machine +define service { + service_description Single Nova Object Store + hostgroup_name nova-single-machine + check_command check_nova_objectstore + use default-service +} +define service { + service_description Single Nova API + hostgroup_name nova-single-machine + check_command check_nova_api + use default-service +} +define service { + service_description Single Nova Scheduler + hostgroup_name nova-single-machine + check_command check_nova_scheduler + use default-service +} +define service { + service_description Single Nova Network + hostgroup_name nova-single-machine + check_command check_nova_network + use default-service +} +define service { + service_description Single Nova Compute + hostgroup_name nova-single-machine + check_command check_nova_compute + use default-service +} +#define service { +# service_description Single Nova Volume +# hostgroup_name nova-single-machine +# check_command check_nova_volume +# use default-service +#} +define service { + service_description Single Nova System + hostgroup_name nova-single-machine + check_command check_nova_manage + use default-service +} +define service { + service_description Single Nova Mysql + hostgroup_name nova-single-machine + check_command check_nova_mysql + use default-service +} +define service { + service_description Single Nova RabbitMQ + hostgroup_name nova-single-machine + check_command check_nova_rabbit + use default-service +} +# Until needed +#define service { +# service_description Single Nova LDAP +# hostgroup_name nova-single-machine +# check_command check_nova_ldap +# use default-service +#} +<% end -%> + +<% unless @service_hosts['nova-multi-controller'].nil? -%> +# These are the services to watch on a nova-single-machine +define service { + service_description Multi Nova Object Store + hostgroup_name nova-multi-controller + check_command check_nova_objectstore + use default-service +} +define service { + service_description Multi Nova Scheduler + hostgroup_name nova-multi-controller + check_command check_nova_scheduler + use default-service +} +define service { + service_description Multi Nova Network + hostgroup_name nova-multi-controller + check_command check_nova_network + use default-service +} +define service { + service_description Multi Nova System + hostgroup_name nova-multi-controller + check_command check_nova_manage + use default-service +} +define service { + service_description Multi Nova Mysql + hostgroup_name nova-multi-controller + check_command check_nova_mysql + use default-service +} +define service { + service_description Multi Nova RabbitMQ + hostgroup_name nova-multi-controller + check_command check_nova_rabbit + use default-service +} +# Until needed +#define service { +# service_description Multi Nova LDAP +# hostgroup_name nova-multi-controller +# check_command check_nova_ldap +# use default-service +#} +<% end -%> + +<% unless @service_hosts['nova-multi-compute'].nil? -%> +# These are the services to watch on a nova-multi-compute +define service { + service_description Multi Nova Compute + hostgroup_name nova-multi-compute + check_command check_nova_compute + use default-service +} +#define service { +# service_description Multi Nova Volume +# hostgroup_name nova-multi-compute +# check_command check_nova_volume +# use default-service +#} +<% end -%> + +#### Services to watch for swift-compoents +<% if !@service_hosts['swift-proxy'].nil? or !@service_hosts['swift-proxy-acct'].nil? then %> + +# check the proxy +define service { + service_description swift proxy process + hostgroup_name swift-proxy-acct + check_command check_swift-proxy + use default-service +} + +<% %w{ object container }.each do |svc| %> +define service { + service_description swift <%= svc%> process + hostgroup_name swift-storage + check_command check_swift-<%= svc%> + use default-service +} + +define service { + service_description swift <%= svc%> replicator process + hostgroup_name swift-storage + check_command check_swift-<%= svc%>-replicator + use default-service +} + +define service { + service_description swift <%= svc%> updater process + hostgroup_name swift-storage + check_command check_swift-<%= svc%>-updater + use default-service +} + +define service { + service_description swift process <%= svc%> auditor + hostgroup_name swift-storage + check_command check_swift-<%= svc%>-auditor + use default-service +} + +define service { + service_description swift http port for <%= svc%> server + hostgroup_name swift-storage + check_command check_port_swift-<%= svc%> + use default-service +} + +<% end %> + +define service { + service_description swift http port for account server + hostgroup_name swift-storage + check_command check_port_swift-account + use default-service +} + +define service { + service_description swift http port for proxy server + hostgroup_name swift-proxy-acct + check_command check_port_swift-proxy + use default-service +} + + +<% %w{swift-account swift-account-reaper swift-account-auditor swift-account-replicator}.each do |svc| %> +define service { + service_description swift process <%= svc %> + hostgroup_name swift-storage + check_command check_<%= svc %> + use default-service +} +<% end %> + +<% end ## end of swift-proxy present condition %> + + +<% unless @service_hosts['glance-server'].nil? then %> +<% %w{glance-api glance-registry}.each do |svc| %> +define service { + service_description Glance process <%= svc %> + hostgroup_name glance-server + check_command check_<%= svc %> + use default-service +} +define service { + service_description Port check for <%= svc%> server + hostgroup_name glance-server + check_command check_port_<%= svc%> + use default-service +} +<% end %> +<% end ## end of glance present condition %> diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/templates.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/templates.cfg.erb new file mode 100644 index 00000000000..91cc04df354 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/templates.cfg.erb @@ -0,0 +1,78 @@ +define contact { + name default-contact + service_notification_period 24x7 + host_notification_period 24x7 + service_notification_options w,u,c,r,f,s + host_notification_options d,u,r,f,s +# service_notification_commands service-notify-by-email, service-notify-by-jabber + service_notification_commands service-notify-by-email +# host_notification_commands host-notify-by-email, host-notify-by-sms-gateway, host-notify-by-jabber + host_notification_commands host-notify-by-email + register 0 +} + +define contact { + name sms-contact + service_notification_period 24x7 + host_notification_period 24x7 + service_notification_options w,u,c,r,f,s + host_notification_options d,u,r,f,s + service_notification_commands service-notify-by-sms-gateway + host_notification_commands host-notify-by-sms-gateway + register 0 +} + +define host { + name default-host + notifications_enabled 1 + event_handler_enabled 1 + flap_detection_enabled 1 + failure_prediction_enabled 1 +# process_perf_data 1 + process_perf_data 0 + retain_status_information 1 + retain_nonstatus_information 1 + notification_period 24x7 + register 0 +} + +define host { + name server + use default-host + check_period 24x7 + check_interval <%= nagios_interval(nagios_attr(:default_host)[:check_interval]) %> + retry_interval <%= nagios_interval(nagios_attr(:default_host)[:retry_interval]) %> + max_check_attempts <%= nagios_attr(:default_host)[:max_check_attempts] %> + check_command check-host-alive + notification_interval <%= nagios_interval(nagios_attr(:default_host)[:notification_interval]) %> + notification_options d,u,r + contact_groups <%= nagios_attr(:default_contact_groups).join(",") %> + register 0 +} + +define service { + name default-service + active_checks_enabled 1 + passive_checks_enabled 1 + parallelize_check 1 + obsess_over_service 1 + check_freshness 0 + notifications_enabled 1 + event_handler_enabled 1 + flap_detection_enabled 1 + failure_prediction_enabled 1 +# process_perf_data 1 + process_perf_data 0 + retain_status_information 1 + retain_nonstatus_information 1 + is_volatile 0 + check_period 24x7 + max_check_attempts <%= nagios_attr(:default_service)[:max_check_attempts] %> + check_interval <%= nagios_interval(nagios_attr(:default_service)[:check_interval]) %> + retry_interval <%= nagios_interval(nagios_attr(:default_service)[:retry_interval]) %> + contact_groups <%= nagios_attr(:default_contact_groups).join(",") %> + notification_options w,u,c,r + notification_interval <%= nagios_interval(nagios_attr(:default_service)[:notification_interval]) %> + notification_period 24x7 + register 0 +} diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/timeperiods.cfg.erb b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/timeperiods.cfg.erb new file mode 100644 index 00000000000..24adc00da66 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/cookbooks/nagios/templates/default/timeperiods.cfg.erb @@ -0,0 +1,14 @@ +# Time period definitions +# Automatically generated by Chef + +define timeperiod { + timeperiod_name 24x7 + alias 24 Hours A Day, 7 Days A Week + sunday 00:00-24:00 + monday 00:00-24:00 + tuesday 00:00-24:00 + wednesday 00:00-24:00 + thursday 00:00-24:00 + friday 00:00-24:00 + saturday 00:00-24:00 +} \ No newline at end of file diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/crowbar/bc-template-nagios.json b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/crowbar/bc-template-nagios.json new file mode 100644 index 00000000000..95fec746822 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/crowbar/bc-template-nagios.json @@ -0,0 +1,28 @@ +{ + "id": "bc-template-nagios", + "description": "common monitoring service for the cluster that can be used by other barclamps", + "attributes": { + "nagios": { + "admin_ip_eval": "Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, \"admin\").address", + "admin_interface_eval": "Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, \"admin\").interface" + } + }, + "deployment": { + "nagios": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "nagios-server", "nagios-client" ] + ], + "config": { + "environment": "nagios-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/crowbar/bc-template-nagios.schema b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/crowbar/bc-template-nagios.schema new file mode 100644 index 00000000000..5435dfd548f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/crowbar/bc-template-nagios.schema @@ -0,0 +1,70 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-nagios-|^bc-template-nagios$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "nagios": { + "type": "map", + "required": true, + "mapping": { + "admin_ip_eval": { "type": "str", "required": true }, + "admin_interface_eval": { "type": "str", "required": true } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "nagios": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/users/nagiosadmin.json b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/users/nagiosadmin.json new file mode 100644 index 00000000000..99be9d7a517 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/users/nagiosadmin.json @@ -0,0 +1,11 @@ +{ + "id": "nagiosadmin", + "groups": "sysadmin", + "htpasswd": "Ah4LiEWT3GBMs", + "openid": "", + "nagios": { + "pager": "nagiosadmin_pager@example.com", + "email": "nagiosadmin@example.com" + } +} + diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/users/nagiosadmin.schema b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/users/nagiosadmin.schema new file mode 100644 index 00000000000..aad271bb6f3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/data_bags/users/nagiosadmin.schema @@ -0,0 +1,18 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "assert": "val == 'nagiosadmin'" }, + "groups": { "type": "str", "required": true }, + "htpasswd": { "type": "str", "required": true }, + "openid": { "type": "str" }, + "nagios": { + "type": "map", + "required": true, + "mapping": { + "pager": { "type": "str", "required": true }, + "email": { "type": "str", "required": true, "name": "Email" } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/roles/nagios-client.rb b/crowbar/change-image/dell/barclamps/nagios/chef/roles/nagios-client.rb new file mode 100644 index 00000000000..ea68134d736 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/roles/nagios-client.rb @@ -0,0 +1,8 @@ + +name "nagios-client" +description "NAGIOS Client Role - Nodes in the environment that should be monitored" +run_list( + "recipe[nagios::client]" +) +default_attributes() +override_attributes() diff --git a/crowbar/change-image/dell/barclamps/nagios/chef/roles/nagios-server.rb b/crowbar/change-image/dell/barclamps/nagios/chef/roles/nagios-server.rb new file mode 100644 index 00000000000..f85d1eddcac --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/chef/roles/nagios-server.rb @@ -0,0 +1,13 @@ + +name "nagios-server" +description "NAGIOS Server Role - NAGIOS master for the cloud" +run_list( + "recipe[nagios::server]" +) +default_attributes( + "nagios" => { + "server_auth_method" => "htauth" + } +) +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/nagios/command_line/crowbar_nagios b/crowbar/change-image/dell/barclamps/nagios/command_line/crowbar_nagios new file mode 100755 index 00000000000..556a79a2638 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/command_line/crowbar_nagios @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "nagios" + +main diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/nagios/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/compat b/crowbar/change-image/dell/barclamps/nagios/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/control b/crowbar/change-image/dell/barclamps/nagios/debian/control new file mode 100644 index 00000000000..e887a4c260f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-nagios +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-nagios +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the nagios barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/copyright b/crowbar/change-image/dell/barclamps/nagios/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/postinst b/crowbar/change-image/dell/barclamps/nagios/debian/postinst new file mode 100644 index 00000000000..becf273afea --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-nagios/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-nagios/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-nagios/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/rules b/crowbar/change-image/dell/barclamps/nagios/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/nagios/debian/source/format b/crowbar/change-image/dell/barclamps/nagios/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/nagios/version.sh b/crowbar/change-image/dell/barclamps/nagios/version.sh new file mode 100644 index 00000000000..7ade0263e47 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/nagios/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=nagios +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/network/Makefile b/crowbar/change-image/dell/barclamps/network/Makefile new file mode 100644 index 00000000000..f704a64b5db --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-network" + +distclean: + @echo "Dist-Cleaning barclamp-network" + +all: clean build install + +build: + @echo "Building barclamp-network" + +install: + @echo "Installing barclamp-network" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-network + cp -r chef ${DESTDIR}/usr/share/barclamp-network + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/network/app/controllers/network_controller.rb b/crowbar/change-image/dell/barclamps/network/app/controllers/network_controller.rb new file mode 100644 index 00000000000..7be4fd6353b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/app/controllers/network_controller.rb @@ -0,0 +1,49 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class NetworkController < BarclampController + # Make a copy of the barclamp controller help + self.help_contents = Array.new(superclass.help_contents) + def initialize + @service_object = NetworkService.new logger + end + + # Below here handles ip address assignment. + add_help(:allocate_ip,[:id,:network,:range,:name],[:post]) + def allocate_ip + id = params[:id] # Network id + network = params[:network] + range = params[:range] + name = params[:name] + + ret = @service_object.allocate_ip(id, network, range, name) + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + + # Below here handles ip address assignment. + add_help(:enable_interface,[:id,:network,:name],[:post]) + def enable_interface + id = params[:id] # Network id + network = params[:network] + name = params[:name] + + ret = @service_object.enable_interface(id, network, name) + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + +end + diff --git a/crowbar/change-image/dell/barclamps/network/app/models/network_service.rb b/crowbar/change-image/dell/barclamps/network/app/models/network_service.rb new file mode 100644 index 00000000000..42c9bec6768 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/app/models/network_service.rb @@ -0,0 +1,349 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class NetworkService < ServiceObject + + def initialize(thelogger) + @bc_name = "network" + @logger = thelogger + end + + def acquire_ip_lock + acquire_lock "ip" + end + + def release_ip_lock(f) + release_lock f + end + + def allocate_ip(bc_instance, network, range, name) + @logger.debug("Network allocate_ip: entering #{name} #{network} #{range}") + + return [404, "No network specified"] if network.nil? + return [404, "No range specified"] if range.nil? + return [404, "No name specified"] if name.nil? + + # Find the node + node = NodeObject.find_node_by_name name + @logger.error("Network allocate_ip: return node not found: #{name} #{network} #{range}") if node.nil? + return [404, "No node found"] if node.nil? + + # Find an interface based upon config + role = RoleObject.find_role_by_name "network-config-#{bc_instance}" + @logger.error("Network allocate_ip: No network data found: #{name} #{network} #{range}") if role.nil? + return [404, "No network data found"] if role.nil? + + mode = role.default_attributes["network"]["mode"] + + # If we already have on allocated, return success + net_info = node.get_network_by_type(network) + unless net_info.nil? or net_info["address"].nil? + intf = net_info["interface"] + net_info = fix_interface(node, net_info, mode) + unless net_info.nil? + node["crowbar"]["network"][intf] = nil + node["crowbar"]["network"][net_info["interface"]] = net_info + node.save + end + @logger.error("Network allocate_ip: node already has address: #{name} #{network} #{range}") + return [200, net_info] + end + + found = false + begin # Rescue block + f = acquire_ip_lock + db = ProposalObject.find_data_bag_item "crowbar/#{network}_network" + + subnet = db["network"]["subnet"] + vlan = db["network"]["vlan"] + use_vlan = db["network"]["use_vlan"] + add_bridge = db["network"]["add_bridge"] + broadcast = db["network"]["broadcast"] + router = db["network"]["router"] + netmask = db["network"]["netmask"] + rangeH = db["network"]["ranges"][range] + rangeH = db["network"]["ranges"]["host"] if rangeH.nil? + + tanswer = get_interface_info node, network, mode, use_vlan, vlan + if !tanswer + @logger.info("Network allocate_ip: return failed to find interface: #{name} #{network} #{range}") + return [404, "Failed to find interface"] + end + interface, interface_list, mac = tanswer + + index = IPAddr.new(rangeH["start"]) & ~IPAddr.new(netmask) + index = index.to_i + stop_address = IPAddr.new(rangeH["end"]) & ~IPAddr.new(netmask) + stop_address = IPAddr.new(subnet) | (stop_address.to_i + 1) + address = IPAddr.new(subnet) | index + + # Did we already allocate this, but the node lose it? + unless db["allocated_by_name"][node.name].nil? + found = true + address = db["allocated_by_name"][node.name]["address"] + end + + # Let's search for an empty one. + while !found do + if db["allocated"][address.to_s].nil? + found = true + break + end + index = index + 1 + address = IPAddr.new(subnet) | index + break if address == stop_address + end + + if found + db["allocated_by_name"][node.name] = { "machine" => node.name, "interface" => interface, "address" => address.to_s } + db["allocated"][address.to_s] = { "machine" => node.name, "interface" => interface, "address" => address.to_s } + db.save + end + rescue Exception => e + @logger.error("Error finding address: #{e.message}") + ensure + release_ip_lock(f) + end + + @logger.info("Network allocate_ip: no address available: #{name} #{network} #{range}") if !found + return [404, "No Address Available"] if !found + + # Save the information. + net_info = { "interface" => interface, "address" => address.to_s, "netmask" => netmask, "mac" => mac, "node" => name, "router" => router, "subnet" => subnet, "broadcast" => broadcast, "usage" => network, "interface_list" => interface_list, "use_vlan" => use_vlan, "vlan" => vlan, "add_bridge" => add_bridge } + node["crowbar"]["network"][interface] = net_info + node.save + + @logger.info("Network allocate_ip: Assigned: #{name} #{network} #{range} #{net_info["address"]}") + [200, net_info] + end + + def create_proposal + @logger.debug("Network create_proposal: entering") + base = super + + base["attributes"]["network"]["networks"].each do |k,net| + @logger.debug("Network: creating #{k} in the network") + bc = Chef::DataBagItem.new + bc.data_bag "crowbar" + bc["id"] = "#{k}_network" + bc["network"] = net + bc["allocated"] = {} + bc["allocated_by_name"] = {} + db = ProposalObject.new bc + db.save + end + + @logger.debug("Network create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("Network transition: Entering #{name} for #{state}") + + if state == "discovered" + db = ProposalObject.find_proposal "network", inst + role = RoleObject.find_role_by_name "network-config-#{inst}" + + @logger.debug("Network transition: make sure that network role is on all nodes: #{name} for #{state}") + result = add_role_to_instance_and_node("network", inst, name, db, role, "network") + + @logger.debug("Network transition: Exiting #{name} for #{state} discovered path") + return [200, {}] if result + return [400, "Failed to add role to node"] unless result + end + + @logger.debug("Network transition: Exiting #{name} for #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + + def apply_role_pre_chef_call(old_role, role, all_nodes) + @logger.debug("Network apply_role_pre_chef_call: entering #{all_nodes.inspect}") + return if all_nodes.empty? + + old_mode = nil + old_mode = old_role.default_attributes["network"]["mode"] unless old_role.nil? or old_role.default_attributes["network"].nil? + new_mode = nil + new_mode = role.default_attributes["network"]["mode"] unless role.nil? or role.default_attributes["network"].nil? + + return if old_mode == new_mode + + all_nodes.each do |n| + node = NodeObject.find_node_by_name n + + save_it = false + new_hash = {} + node["crowbar"]["network"].each do |intf, o_net_info| + net_info = fix_interface(node, o_net_info, new_mode) + unless net_info.nil? + save_it = true + new_hash[net_info["interface"]] = net_info + else + new_hash[intf] = o_net_info + end + end + node["crowbar"]["network"] = new_hash + + @logger.debug("Network apply_role_pre_chef_call: saving node") if save_it + node.save if save_it + end + @logger.debug("Network apply_role_pre_chef_call: leaving") + end + + def fix_interface(node, net_info, new_mode) + @logger.debug("Network fix_interface: #{node.name} #{net_info.inspect} #{new_mode}") + tanswer = get_interface_info node, net_info["usage"], new_mode, net_info["use_vlan"], net_info["vlan"] + if !tanswer + @logger.info("fix_interface: return failed to find interface: #{node.name}") + return nil + end + interface, interface_list, mac = tanswer + + if interface != net_info["interface"] + address = net_info["address"] + + begin # Rescue block + f = acquire_ip_lock + db = ProposalObject.find_data_bag_item "crowbar/#{net_info["usage"]}_network" + db["allocated_by_name"][node.name] = { "machine" => node.name, "interface" => interface, "address" => address } + db["allocated"][address] = { "machine" => node.name, "interface" => interface, "address" => address } + db.save + rescue Exception => e + @logger.error("Error finding address: #{e.message}") + ensure + release_ip_lock(f) + end + + net_info["interface"] = interface + net_info["mac"] = mac + net_info["interface_list"] = interface_list + + @logger.debug("Network fix_interface: leaving true: #{node.name} #{net_info.inspect} #{new_mode}") + return net_info + end + + @logger.debug("Network fix_interface: leaving false: #{node.name} #{net_info.inspect} #{new_mode}") + return nil + end + + def get_interface_info(node, network, mode, use_vlan, vlan) + @logger.debug("Network get_interface_info: entering #{node.name} #{network} #{mode} #{use_vlan} #{vlan}") + single = mode == "single" + dual = mode == "dual" + team = mode == "team" + interface_list = node.interface_list + + if network == "bmc" + interface = "bmc" + mac = "bmc" + else + if single or ((network == "admin" or network == "bmc_vlan") and dual) or interface_list.size == 1 + linterface = interface = interface_list.first + interface_list = [ interface ] + if use_vlan + interface = "#{interface}.#{vlan}" + end + elsif dual + linterface = interface = interface_list[1] + interface_list = [ interface ] + if use_vlan + interface = "#{interface}.#{vlan}" + end + elsif team + interface = "bond0" + linterface = interface_list.first + if use_vlan + interface = "#{interface}.#{vlan}" + interface_list = [ "bond0" ] + end + else + return false + end + + mac = "" + node["network"]["interfaces"][linterface]["addresses"].each do |k,addr| + mac = k.downcase + break if addr[:family] == "lladdr" + end + end + + answer = [ interface, interface_list, mac ] + @logger.debug("Network get_interface_info: leaving #{answer.inspect}") + answer + end + + def enable_interface(bc_instance, network, name) + @logger.debug("Network enable_interface: entering #{name} #{network}") + + return [404, "No network specified"] if network.nil? + return [404, "No name specified"] if name.nil? + + # Find the node + node = NodeObject.find_node_by_name name + @logger.error("Network enable_interface: return node not found: #{name} #{network}") if node.nil? + return [404, "No node found"] if node.nil? + + # Find an interface based upon config + role = RoleObject.find_role_by_name "network-config-#{bc_instance}" + @logger.error("Network enable_interface: No network data found: #{name} #{network}") if role.nil? + return [404, "No network data found"] if role.nil? + + mode = role.default_attributes["network"]["mode"] + + # If we already have on allocated, return success + net_info = node.get_network_by_type(network) + unless net_info.nil? + intf = net_info["interface"] + net_info = fix_interface(node, net_info, mode) + unless net_info.nil? + node["crowbar"]["network"][intf] = nil + node["crowbar"]["network"][net_info["interface"]] = net_info + node.save + end + @logger.error("Network enable_interface: node already has address: #{name} #{network}") + return [200, net_info] + end + + begin # Rescue block + db = ProposalObject.find_data_bag_item "crowbar/#{network}_network" + + subnet = db["network"]["subnet"] + vlan = db["network"]["vlan"] + use_vlan = db["network"]["use_vlan"] + add_bridge = db["network"]["add_bridge"] + broadcast = db["network"]["broadcast"] + router = db["network"]["router"] + netmask = db["network"]["netmask"] + + tanswer = get_interface_info node, network, mode, use_vlan, vlan + if !tanswer + @logger.info("Network enable_interface: return failed to find interface: #{name} #{network} #{range}") + return [404, "Failed to find interface"] + end + interface, interface_list, mac = tanswer + rescue Exception => e + @logger.error("Error finding address: #{e.message}") + ensure + end + + # Save the information. + net_info = { "interface" => interface, "netmask" => netmask, "mac" => mac, "node" => name, "router" => router, "subnet" => subnet, "broadcast" => broadcast, "usage" => network, "interface_list" => interface_list, "use_vlan" => use_vlan, "vlan" => vlan, "add_bridge" => add_bridge } + node["crowbar"]["network"][interface] = net_info + node.save + + @logger.info("Network enable_interface: Assigned: #{name} #{network}") + [200, net_info] + end + +end diff --git a/crowbar/change-image/dell/barclamps/network/barclamp-network.spec b/crowbar/change-image/dell/barclamps/network/barclamp-network.spec new file mode 100644 index 00000000000..02066e96228 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/barclamp-network.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-network +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: Instantiates network interfaces on the crowbar managed systems. Also manages the address pool +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages network deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-network/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-network/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-network/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/network/build_deb.sh b/crowbar/change-image/dell/barclamps/network/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/network/build_rpm.sh b/crowbar/change-image/dell/barclamps/network/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/metadata.json b/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/metadata.json new file mode 100644 index 00000000000..2d2fc46fc02 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "network": "Installs network" + }, + "maintainer_email": "crowbar@dell.com", + "name": "network", + "conflicting": { + }, + "description": "Installs network", + "long_description": "" + } diff --git a/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/recipes/default.rb b/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/recipes/default.rb new file mode 100644 index 00000000000..b06ba68e823 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/recipes/default.rb @@ -0,0 +1,145 @@ + +include_recipe 'utils' + +nova_network = node[:nova][:flat_interface] if !node[:nova].nil? and !node[:nova][:network_type].nil? +nova_found = false + +interfaces = Array.new +if_map = {} +node["crowbar"]["network"].each do |intf, network| + nova_found = true if !nova_network.nil? and nova_network == intf + if network["add_bridge"] + # Add base interface + interfaces << { + :interface => intf, + :ipaddress => "0.0.0.0", + :vlan => network["vlan"], + :use_vlan => network["use_vlan"], + :interface_list => network["interface_list"] + } + + # Add bridge + interfaces << { + :interface => "br#{network["vlan"]}", + :ipaddress => network["address"], + :subnet => network["subnet"], + :netmask => network["netmask"], + :broadcast => network["broadcast"], + :router => network["router"], + :interface_list => [ intf ] + } + + if_map[intf] = true + if_map["br#{network["vlan"]}"] = true + else + interfaces << { + :interface => intf, + :ipaddress => network["address"], + :subnet => network["subnet"], + :netmask => network["netmask"], + :broadcast => network["broadcast"], + :router => network["router"], + :vlan => network["vlan"], + :use_vlan => network["use_vlan"], + :interface_list => network["interface_list"] + } + if_map[intf] = true + end +end unless node["crowbar"].nil? or node["crowbar"]["network"].nil? + +if !nova_network.nil? and !nova_found + Chef::Log.fatal("Failed to define an interface with IP address for Nova!") + interfaces << { + :interface => nova_network + } +end + +links = `ip -o link`.split("\n") +remove_if = [] +links.each do |link| + if link =~ /^[0-9]+: (.*): / + intf = $1.split("@")[0] + next if if_map[intf] + next if intf == "lo" + next if intf =~ /^vnet/ + if node["crowbar"]["network"][intf].nil? + remove_if << intf + end + end +end + +package "bridge-utils" +package "vlan" +package "ifenslave-2.6" + +# Make sure that the /etc/network/if-up.d/upstart file is gone +# We manage apache2 (and others), it shouldn't +file "/etc/network/if-up.d/upstart" do + action :delete +end + +utils_line "8021q" do + action :add + file "/etc/modules" +end + +bash "load 8021q module" do + code "/sbin/modprobe 8021q" + not_if "lsmod | grep -q 8021q" +end + +if node["network"]["mode"] == "team" + utils_line "bonding mode=6 miimon=100" do + action :add + file "/etc/modules" + end + + bash "load bonding module" do + code "/sbin/modprobe bonding mode=6 miimon=100" + not_if "lsmod | grep -q bonding" + end +end + +bash "ensure no dhclient" do + code "killall dhclient3" + only_if "ps -ef | grep -v grep | grep -q dhclient" +end + +template "/etc/network/interfaces" do + source "interfaces.erb" + variables :interfaces => interfaces + notifies :run, "execute[restart-networking]", :immediately +end + +execute "restart-networking" do + command "/etc/init.d/networking restart" + action :nothing + notifies :run, "execute[delay-networking]", :immediately +end + +execute "delay-networking" do + command "sleep 30" + action :nothing +end + +remove_if = remove_if.sort!{ |a, b| b <=> a } +remove_if.each do |link| + bash "remove #{link} interface" do + code "ifconfig #{link} inet 0.0.0.0 ; ip link delete #{link} ; true" + only_if "ip link show #{link}" + end +end + +if node["network"]["mode"] != "team" + utils_line "bonding mode=6 miimon=100" do + action :remove + file "/etc/modules" + end + + bash "remove bonding module" do + code "/sbin/rmmod bonding" + only_if "lsmod | grep -q bonding" + notifies :run, "execute[restart-networking]", :immediately + end +end + diff --git a/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/templates/default/interfaces.erb b/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/templates/default/interfaces.erb new file mode 100644 index 00000000000..aecd5e2fde1 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/chef/cookbooks/network/templates/default/interfaces.erb @@ -0,0 +1,48 @@ + +auto lo +iface lo inet loopback + +<% @interfaces.each do |interface| -%> +<% next if interface[:interface] == "bmc" -%> + +<% if interface[:interface] =~ /^br/ %> + +auto <%= interface[:interface] %> +iface <%= interface[:interface] %> inet static + bridge_ports <%= interface[:interface_list].first %> + bridge_stp off + bridge_maxwait 0 + bridge_fd 0 + +<% else # nova part -%> + +auto <%= interface[:interface] %> +iface <%= interface[:interface] %> inet static + +<% end # if nova -%> + +<% if !interface[:ipaddress].nil? -%> + address <%= interface[:ipaddress] %> +<% else -%> + address 0.0.0.0 +<% end -%> +<% if !interface[:netmask].nil? -%> + netmask <%= interface[:netmask] %> +<% end -%> +<% if !interface[:broadcast].nil? -%> + broadcast <%= interface[:broadcast] %> +<% end -%> +<% if !interface[:router].nil? -%> + gateway <%= interface[:router] %> +<% end -%> + +<% unless interface[:interface_list].nil? or interface[:interface_list].size <= 1 -%> + down /sbin/ifenslave -d <%= interface[:interface] %> <%= interface[:interface_list].join(" ") %> + up /sbin/ifenslave <%= interface[:interface] %> <%= interface[:interface_list].join(" ") %> +<% end -%> +<% if interface[:use_vlan] and interface[:vlan] -%> + vlan_raw_device <%= interface[:interface_list].first %> +<% end -%> + +<% end # loop -%> + diff --git a/crowbar/change-image/dell/barclamps/network/chef/data_bags/crowbar/bc-template-network.json b/crowbar/change-image/dell/barclamps/network/chef/data_bags/crowbar/bc-template-network.json new file mode 100644 index 00000000000..9b3d3e051b8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/chef/data_bags/crowbar/bc-template-network.json @@ -0,0 +1,116 @@ +{ + "id": "bc-template-network", + "description": "Instantiates network interfaces on the crowbar managed systems. Also manages the address pool", + "attributes": { + "network": { + "mode": "single", + "teaming": { + "mode": 6 + }, + "networks": { + "storage": { + "vlan": 200, + "use_vlan": true, + "add_bridge": false, + "subnet": "192.168.125.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.125.255", + "ranges": { + "host": { "start": "192.168.125.10", "end": "192.168.125.239" } + } + }, + "public": { + "vlan": 300, + "use_vlan": true, + "add_bridge": false, + "subnet": "192.168.122.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.122.255", + "router": "192.168.122.1", + "ranges": { + "host": { "start": "192.168.122.2", "end": "192.168.122.49" }, + "dhcp": { "start": "192.168.122.50", "end": "192.168.122.254" }, + "router": { "start": "192.168.122.1", "end": "192.168.122.1" } + } + }, + "nova_fixed": { + "vlan": 500, + "use_vlan": true, + "add_bridge": true, + "subnet": "192.168.123.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.123.255", + "router": "192.168.123.1", + "ranges": { + "host": { "start": "192.168.123.2", "end": "192.168.123.49" }, + "dhcp": { "start": "192.168.123.50", "end": "192.168.123.254" } + } + }, + "nova_floating": { + "vlan": 400, + "use_vlan": true, + "add_bridge": false, + "subnet": "192.168.126.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.126.255", + "ranges": { + "host": { "start": "192.168.126.2", "end": "192.168.126.255" } + } + }, + "bmc": { + "vlan": 100, + "use_vlan": false, + "add_bridge": false, + "subnet": "192.168.124.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.124.255", + "ranges": { + "host": { "start": "192.168.124.162", "end": "192.168.124.240" } + } + }, + "bmc_vlan": { + "vlan": 100, + "use_vlan": true, + "add_bridge": false, + "subnet": "192.168.124.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.124.255", + "ranges": { + "host": { "start": "192.168.124.161", "end": "192.168.124.161" } + } + }, + "admin": { + "vlan": 100, + "use_vlan": false, + "add_bridge": false, + "subnet": "192.168.124.0", + "netmask": "255.255.255.0", + "broadcast": "192.168.124.255", + "router": "192.168.124.1", + "ranges": { + "admin": { "start": "192.168.124.10", "end": "192.168.124.11" }, + "dhcp": { "start": "192.168.124.21", "end": "192.168.124.80" }, + "host": { "start": "192.168.124.81", "end": "192.168.124.160" }, + "switch": { "start": "192.168.124.241", "end": "192.168.124.250" } + } + } + } + } + }, + "deployment": { + "network": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "network" ] + ], + "config": { + "environment": "network-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ "discovered" ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/network/chef/data_bags/crowbar/bc-template-network.schema b/crowbar/change-image/dell/barclamps/network/chef/data_bags/crowbar/bc-template-network.schema new file mode 100644 index 00000000000..e5fa8caf5b3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/chef/data_bags/crowbar/bc-template-network.schema @@ -0,0 +1,109 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-network-|^bc-template-network$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "network": { + "type": "map", + "required": true, + "mapping": { + "mode": { "type": "str", "required": true, "pattern": "/^single$|^dual$|^team$/" }, + "teaming": { + "type": "map", + "required": true, + "mapping": { + "mode": { "type": "int", "required": true } + } + }, + "networks": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "map", + "required": true, + "mapping": { + "vlan": { "type": "int", "required": true }, + "use_vlan": { "type": "bool", "required": true }, + "add_bridge": { "type": "bool", "required": true }, + "subnet": { "type": "str", "required": true, "name": "IpAddress" }, + "netmask": { "type": "str", "required": true, "name": "IpAddress" }, + "broadcast": { "type": "str", "required": true, "name": "IpAddress" }, + "router": { "type": "str", "name": "IpAddress" }, + "ranges": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "map", + "required": true, + "mapping": { + "start": { "type": "str", "required": true, "name": "IpAddress" }, + "end": { "type": "str", "required": true, "name": "IpAddress" } + } + } + } + } + } + } + } + } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "network": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/network/chef/roles/network.rb b/crowbar/change-image/dell/barclamps/network/chef/roles/network.rb new file mode 100644 index 00000000000..9ea75c47abb --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/chef/roles/network.rb @@ -0,0 +1,7 @@ + +name "network" +description "Network role - Setups the network" +run_list("recipe[network]") +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/network/command_line/crowbar_network b/crowbar/change-image/dell/barclamps/network/command_line/crowbar_network new file mode 100755 index 00000000000..06a007a4be3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/command_line/crowbar_network @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "network" + + +@commands["allocate_ip"] = [ "allocate_ip(ARGV.shift,ARGV.shift,ARGV.shift,ARGV.shift)", "allocate_ip - Allocate an ip for a name on a network from a range" ] +@commands["enable_interface"] = [ "enable_interface(ARGV.shift,ARGV.shift,ARGV.shift)", "enable_interface - Ensure that an interface is present for the specified network" ] + +def enable_interface(name, node, network) + usage -1 if name.nil? or name == "" + usage -1 if node.nil? or node == "" + usage -1 if network.nil? or network == "" + + @data = { "name" => node, "network" => network }.to_json + struct = post_json("/enable_interface/#{name}", @data) + + if struct[1] == 200 + [ "Edited #{name} #{struct[0].inspect}", 0 ] + elsif struct[1] == 404 + [ "Failed to edit: #{name} : Not Found", 1 ] + elsif struct[1] == 400 + [ "Failed to edit: #{name} : Errors in data\n#{struct[0]}", 1 ] + else + [ "Failed to talk to service enable_interface: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +def allocate_ip(name, node, network, range) + usage -1 if name.nil? or name == "" + usage -1 if network.nil? or network == "" + usage -1 if node.nil? or node == "" + usage -1 if range.nil? or range == "" + + @data = { "name" => node, "network" => network, "range" => range }.to_json + struct = post_json("/allocate_ip/#{name}", @data) + + if struct[1] == 200 + [ "Edited #{name} #{struct[0].inspect}", 0 ] + elsif struct[1] == 404 + [ "Failed to edit: #{name} : Not Found", 1 ] + elsif struct[1] == 400 + [ "Failed to edit: #{name} : Errors in data\n#{struct[0]}", 1 ] + else + [ "Failed to talk to service allocate_ip: #{struct[1]}: #{struct[0]}", 1 ] + end +end + +main diff --git a/crowbar/change-image/dell/barclamps/network/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/network/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/network/debian/compat b/crowbar/change-image/dell/barclamps/network/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/network/debian/control b/crowbar/change-image/dell/barclamps/network/debian/control new file mode 100644 index 00000000000..f891f172e63 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-network +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-network +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the network barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/network/debian/copyright b/crowbar/change-image/dell/barclamps/network/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/network/debian/postinst b/crowbar/change-image/dell/barclamps/network/debian/postinst new file mode 100644 index 00000000000..0e820597d27 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-network/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-network/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-network/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/network/debian/rules b/crowbar/change-image/dell/barclamps/network/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/network/debian/source/format b/crowbar/change-image/dell/barclamps/network/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/network/version.sh b/crowbar/change-image/dell/barclamps/network/version.sh new file mode 100644 index 00000000000..7d4b35fb826 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/network/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=network +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/ntp/Makefile b/crowbar/change-image/dell/barclamps/ntp/Makefile new file mode 100644 index 00000000000..ca9f1b84599 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-ntp" + +distclean: + @echo "Dist-Cleaning barclamp-ntp" + +all: clean build install + +build: + @echo "Building barclamp-ntp" + +install: + @echo "Installing barclamp-ntp" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-ntp + cp -r chef ${DESTDIR}/usr/share/barclamp-ntp + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/ntp/app/controllers/ntp_controller.rb b/crowbar/change-image/dell/barclamps/ntp/app/controllers/ntp_controller.rb new file mode 100644 index 00000000000..3ec6371631a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/app/controllers/ntp_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class NtpController < BarclampController + def initialize + @service_object = NtpService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/ntp/app/models/ntp_service.rb b/crowbar/change-image/dell/barclamps/ntp/app/models/ntp_service.rb new file mode 100644 index 00000000000..bd60697bfc1 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/app/models/ntp_service.rb @@ -0,0 +1,63 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class NtpService < ServiceObject + + def initialize(thelogger) + @bc_name = "ntp" + @logger = thelogger + end + + def create_proposal + @logger.debug("NTP create_proposal: entering") + base = super + @logger.debug("NTP create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("NTP transition: make sure that network role is on all nodes: #{name} for #{state}") + + # + # If we are discovering the node, make sure that we add the ntp client or server to the node + # + if state == "discovered" + @logger.debug("NTP transition: discovered state for #{name} for #{state}") + db = ProposalObject.find_proposal "ntp", inst + role = RoleObject.find_role_by_name "ntp-config-#{inst}" + + if role.override_attributes["ntp"]["elements"]["ntp-server"].nil? or + role.override_attributes["ntp"]["elements"]["ntp-server"].empty? + @logger.debug("NTP transition: make sure that ntp-server role is on first: #{name} for #{state}") + result = add_role_to_instance_and_node("ntp", inst, name, db, role, "ntp-server") + else + node = NodeObject.find_node_by_name name + unless node.role? "ntp-server" + @logger.debug("NTP transition: make sure that ntp-client role is on all nodes but first: #{name} for #{state}") + result = add_role_to_instance_and_node("ntp", inst, name, db, role, "ntp-client") + end + end + + @logger.debug("NTP transition: leaving from discovered state for #{name} for #{state}") + return [200, {}] if result + return [400, "Failed to add role to node"] unless result + end + + @logger.debug("NTP transition: leaving for #{name} for #{state}") + [200, NodeObject.find_node_by_name(name).to_hash ] + end + +end + diff --git a/crowbar/change-image/dell/barclamps/ntp/barclamp-ntp.spec b/crowbar/change-image/dell/barclamps/ntp/barclamp-ntp.spec new file mode 100644 index 00000000000..dd42445c2bf --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/barclamp-ntp.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-ntp +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: Common NTP service for the cluster. An NTP server or servers can be specified and all other nodes will be clients of them. +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages ntp deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-ntp/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-ntp/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-ntp/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/ntp/build_deb.sh b/crowbar/change-image/dell/barclamps/ntp/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/ntp/build_rpm.sh b/crowbar/change-image/dell/barclamps/ntp/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/libraries/eval.rb b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/libraries/eval.rb new file mode 100644 index 00000000000..9e651bc1966 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/libraries/eval.rb @@ -0,0 +1,44 @@ +# +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: andi abes +# + +class Ntp +class Evaluator + + def initialize(node) + @b = binding + end + + def eval_with_context(str) + eval(str,@b) + end + + def log_eval_vars() + eval("Chef::Log.info('locals:'+local_variables.join(':') + '\nglobals:'+global_variables.join(':'))") + end + + + def self.get_value_by_type(node, type) + location = node[:ntp][type] + e = Evaluator.new(node) + val = e.eval_with_context(location) + Chef::Log.info("Looking at #{location} for #{type}. Got: #{val}") + val + end + +end +end diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/metadata.json b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/metadata.json new file mode 100644 index 00000000000..77c4e4b102f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "ntp-server": "Installs the ntp server" + }, + "maintainer_email": "crowbar@dell.com", + "name": "ntp-server", + "conflicting": { + }, + "description": "Installs the ntp server", + "long_description": "" +} diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/recipes/default.rb b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/recipes/default.rb new file mode 100644 index 00000000000..3c48f596684 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/recipes/default.rb @@ -0,0 +1,38 @@ + +package "ntp" + +if node.role?("ntp-client") + unless Chef::Config[:solo] + env_filter = " AND environment:#{node[:ntp][:config][:environment]}" + servers = search(:node, "roles:ntp\\-server#{env_filter}") + end + ntp_servers = nil + ntp_servers = servers.map {|n| Ntp::Evaluator.get_value_by_type(n, :admin_ip_eval) } unless servers.nil? +else + ntp_servers = node[:ntp][:external_servers] +end + +service "ntp" do + supports :restart => true, :status => true, :reload => true + running true + enabled true + action [ :enable, :start ] +end + +template "/etc/ntp.conf" do + owner "root" + group "root" + mode 0644 + source "ntp.conf.erb" + variables(:ntp_servers => ntp_servers) + notifies :restart, "service[ntp]" +end + +# +# Make sure the ntpdate helper is removed to speed up network restarts +# This script manages ntp for the client +# +file "/etc/network/if-up.d/ntpdate" do + action :delete +end + diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/templates/default/ntp.conf.erb b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/templates/default/ntp.conf.erb new file mode 100644 index 00000000000..fae0ff8c5c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/cookbooks/ntp/templates/default/ntp.conf.erb @@ -0,0 +1,53 @@ +# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help + +driftfile /var/lib/ntp/ntp.drift + + +# Enable this if you want statistics to be logged. +#statsdir /var/log/ntpstats/ + +statistics loopstats peerstats clockstats +filegen loopstats file loopstats type day enable +filegen peerstats file peerstats type day enable +filegen clockstats file clockstats type day enable + + +# You do need to talk to an NTP server or two (or three). +<% if @ntp_servers.nil? or @ntp_servers.empty? -%> +server 127.127.1.0 +fudge 127.127.1.0 stratum 1 +<% else -%> +<% @ntp_servers.each do |ntp_server| -%> +server <%= ntp_server %> +<% end -%> +<% end -%> + +# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for +# details. The web page +# might also be helpful. +# +# Note that "restrict" applies to both servers and clients, so a configuration +# that might be intended to block requests from certain clients could also end +# up blocking replies from your own upstream servers. + +# By default, exchange time with everybody, but don't allow configuration. +restrict -4 default kod notrap nomodify nopeer noquery +restrict -6 default kod notrap nomodify nopeer noquery + +# Local users may interrogate the ntp server more closely. +restrict 127.0.0.1 +restrict ::1 + +# Clients from this (example!) subnet have unlimited access, but only if +# cryptographically authenticated. +#restrict 192.168.123.0 mask 255.255.255.0 notrust + + +# If you want to provide time to your local subnet, change the next line. +# (Again, the address is an example only.) +#broadcast 192.168.123.255 + +# If you want to listen to time broadcasts on your local subnet, de-comment the +# next lines. Please do this only if you trust everybody on the network! +#disable auth +#broadcastclient diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/data_bags/crowbar/bc-template-ntp.json b/crowbar/change-image/dell/barclamps/ntp/chef/data_bags/crowbar/bc-template-ntp.json new file mode 100644 index 00000000000..1798193e172 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/data_bags/crowbar/bc-template-ntp.json @@ -0,0 +1,28 @@ +{ + "id": "bc-template-ntp", + "description": "Common NTP service for the cluster. An NTP server or servers can be specified and all other nodes will be clients of them.", + "attributes": { + "ntp": { + "external_servers": [ ], + "admin_ip_eval": "Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, \"admin\").address" + } + }, + "deployment": { + "ntp": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "ntp-server", "ntp-client" ] + ], + "config": { + "environment": "ntp-base-config", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/data_bags/crowbar/bc-template-ntp.schema b/crowbar/change-image/dell/barclamps/ntp/chef/data_bags/crowbar/bc-template-ntp.schema new file mode 100644 index 00000000000..7f732a2a88e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/data_bags/crowbar/bc-template-ntp.schema @@ -0,0 +1,74 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-ntp-|^bc-template-ntp$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "ntp": { + "type": "map", + "required": true, + "mapping": { + "external_servers": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + }, + "admin_ip_eval": { "type": "str", "required": true } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "ntp": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/roles/ntp-client.rb b/crowbar/change-image/dell/barclamps/ntp/chef/roles/ntp-client.rb new file mode 100644 index 00000000000..b22c8d0ab6b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/roles/ntp-client.rb @@ -0,0 +1,9 @@ + +name "ntp-client" +description "NTP Client Role - NTP client for the cloud points to Master" +run_list( + "recipe[ntp]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/ntp/chef/roles/ntp-server.rb b/crowbar/change-image/dell/barclamps/ntp/chef/roles/ntp-server.rb new file mode 100644 index 00000000000..90f6ef8da26 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/chef/roles/ntp-server.rb @@ -0,0 +1,9 @@ + +name "ntp-server" +description "NTP Servier Role - NTP master for the cloud" +run_list( + "recipe[ntp]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/ntp/command_line/crowbar_ntp b/crowbar/change-image/dell/barclamps/ntp/command_line/crowbar_ntp new file mode 100755 index 00000000000..8102ab2f059 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/command_line/crowbar_ntp @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "ntp" + +main diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/ntp/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/compat b/crowbar/change-image/dell/barclamps/ntp/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/control b/crowbar/change-image/dell/barclamps/ntp/debian/control new file mode 100644 index 00000000000..ed1d7757a37 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-ntp +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-ntp +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the ntp barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/copyright b/crowbar/change-image/dell/barclamps/ntp/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/postinst b/crowbar/change-image/dell/barclamps/ntp/debian/postinst new file mode 100644 index 00000000000..daa4e0f4d63 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-ntp/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-ntp/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-ntp/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/rules b/crowbar/change-image/dell/barclamps/ntp/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/ntp/debian/source/format b/crowbar/change-image/dell/barclamps/ntp/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/ntp/version.sh b/crowbar/change-image/dell/barclamps/ntp/version.sh new file mode 100644 index 00000000000..fc3a5bbe7d3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/ntp/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=ntp +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/provisioner/Makefile b/crowbar/change-image/dell/barclamps/provisioner/Makefile new file mode 100644 index 00000000000..669b1c5e130 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-provisioner" + +distclean: + @echo "Dist-Cleaning barclamp-provisioner" + +all: clean build install + +build: + @echo "Building barclamp-provisioner" + +install: + @echo "Installing barclamp-provisioner" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-provisioner + cp -r chef ${DESTDIR}/usr/share/barclamp-provisioner + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/provisioner/app/controllers/provisioner_controller.rb b/crowbar/change-image/dell/barclamps/provisioner/app/controllers/provisioner_controller.rb new file mode 100644 index 00000000000..bf4832562f0 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/app/controllers/provisioner_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ProvisionerController < BarclampController + def initialize + @service_object = ProvisionerService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/app/models/provisioner_service.rb b/crowbar/change-image/dell/barclamps/provisioner/app/models/provisioner_service.rb new file mode 100644 index 00000000000..5a150a5f3f2 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/app/models/provisioner_service.rb @@ -0,0 +1,100 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ProvisionerService < ServiceObject + + def initialize(thelogger) + @bc_name = "provisioner" + @logger = thelogger + end + + def create_proposal + @logger.debug("Provisioner create_proposal: entering") + base = super + @logger.debug("Provisioner create_proposal: exiting") + base + end + + def transition(inst, name, state) + @logger.debug("Provisioner transition: entering for #{name} for #{state}") + + role = RoleObject.find_role_by_name "provisioner-config-#{inst}" + + # + # If the node is discovered, add the provisioner base to the node + # + if state == "discovered" + @logger.debug("Provisioner transaction: discovered state for #{name} for #{state}") + db = ProposalObject.find_proposal "provisioner", inst + + # + # Add the first node as the provisioner server + # + if role.override_attributes["provisioner"]["elements"]["provisioner-server"].nil? + @logger.debug("Provisioner transaction: if we have no provisioner add one: #{name} for #{state}") + add_role_to_instance_and_node("provisioner", inst, name, db, role, "provisioner-server") + + # Reload the roles + db = ProposalObject.find_proposal "provisioner", inst + role = RoleObject.find_role_by_name "provisioner-config-#{inst}" + end + + @logger.debug("Provisioner transaction: Make sure that base is on everything: #{name} for #{state}") + result = add_role_to_instance_and_node("provisioner", inst, name, db, role, "provisioner-base") + + if !result + @logger.error("Provisioner transaction: existing discovered state for #{name} for #{state}: Failed") + return [400, "Failed to add role to node"] + else + # Set up the client url + role = RoleObject.find_role_by_name "provisioner-config-#{inst}" + + # Get the server IP address + server_ip = nil + [ "provisioner-server" ].each do |element| + tnodes = role.override_attributes["provisioner"]["elements"][element] + next if tnodes.nil? or tnodes.empty? + tnodes.each do |n| + next if n.nil? + node = NodeObject.find_node_by_name(n) + server_ip = node.get_network_by_type("admin")["address"] + end + end + + unless server_ip.nil? + node = NodeObject.find_node_by_name(name) + node["crowbar"] = {} if node["crowbar"].nil? + node["crowbar"]["links"] = {} if node["crowbar"]["links"].nil? + node["crowbar"]["links"]["Chef"] = "http://#{server_ip}:4040/nodes/#{node.name}" + node.save + end + end + end + + # + # test state machine and call chef-client if state changes + # + node = NodeObject.find_node_by_name(name) + unless node.admin? or role.default_attributes["provisioner"]["dhcp"]["state_machine"][state].nil? + # All non-admin nodes call single_chef_client if the state machine says to. + @logger.info("Provisioner transaction: Run the chef-client locally") + system("sudo /opt/dell/bin/single_chef_client.sh") + end + @logger.debug("Provisioner transaction: exiting for #{name} for #{state}") + [200, node.to_hash ] + end + +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/barclamp-provisioner.spec b/crowbar/change-image/dell/barclamps/provisioner/barclamp-provisioner.spec new file mode 100644 index 00000000000..d03bca60cbd --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/barclamp-provisioner.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-provisioner +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: The roles and recipes to set up the provisioning server and a base environment for all nodes +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages provisioner deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-provisioner/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-provisioner/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-provisioner/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/provisioner/build_deb.sh b/crowbar/change-image/dell/barclamps/provisioner/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/provisioner/build_rpm.sh b/crowbar/change-image/dell/barclamps/provisioner/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/attributes/default.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/attributes/default.rb new file mode 100644 index 00000000000..5c0c3e17133 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/attributes/default.rb @@ -0,0 +1,9 @@ + +default[:dhcp][:interfaces] = [ "eth0" ] +default[:dhcp][:options] = [ + "option dhcp-client-state code 225 = unsigned integer 16", + "option dhcp-client-state 0", + "option dhcp-client-debug code 226 = unsigned integer 16", + "option dhcp-client-debug 0" +] + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/metadata.json b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/metadata.json new file mode 100644 index 00000000000..bd8f15d10e2 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/metadata.json @@ -0,0 +1,81 @@ +{ + "providing": { + }, + "attributes": { + "dhcp": { + "display_name": "DHCP Hash", + "description": "Hash of DHCP attributes", + "type": "hash", + "choice": [ + + ], + "calculated": false, + "required": "optional", + "recipes": [ + + ] + }, + "dhcp/interfaces": { + "display_name": "DHCP Listening Interfaces", + "description": "Interfaces that DHCP should listen on", + "type": "array", + "default": [ + "eth0" + ], + "choice": [ + + ], + "calculated": false, + "required": "optional", + "recipes": [ + + ] + }, + "dhcp/options": { + "display_name": "DHCP Default Options", + "description": "Options that DHCP should use by default", + "type": "array", + "default": [ + + ], + "choice": [ + + ], + "calculated": false, + "required": "optional", + "recipes": [ + + ] + } + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "dhcp": "Installs dhcp" + }, + "maintainer_email": "crowbar@dell.com", + "name": "dhcp", + "conflicting": { + }, + "description": "Installs dhcp", + "long_description": "" +} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/group.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/group.rb new file mode 100644 index 00000000000..b4b59f07b97 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/group.rb @@ -0,0 +1,39 @@ + +action :add do + filename = "/etc/dhcp3/groups.d/#{new_resource.name}.conf" + template filename do + cookbook "dhcp" + source "group.conf.erb" + variables( + :name => new_resource.name, + :options => new_resource.options + ) + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + utils_line "include \"#{filename}\";" do + action :add + file "/etc/dhcp3/groups.d/group_list.conf" + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end +end + +action :remove do + filename = "/etc/dhcp3/groups.d/#{new_resource.name}.conf" + if ::File.exists?(filename) + Chef::Log.info "Removing #{new_resource.name} group from /etc/dhcp3/groups.d/" + file filename do + action :delete + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + new_resource.updated_by_last_action(true) + end + utils_line "include \"#{filename}\";" do + action :remove + file "/etc/dhcp3/groups.d/group_list.conf" + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/host.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/host.rb new file mode 100644 index 00000000000..fae9fcd3ff3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/host.rb @@ -0,0 +1,49 @@ + +action :add do + Chef::Log.debug "Adding #{new_resource.name}.conf to /etc/dhcp3/hosts.d" + filename = "/etc/dhcp3/hosts.d/#{new_resource.name}.conf" + template filename do + cookbook "dhcp" + source "host.conf.erb" + variables( + :name => new_resource.name, + :hostname => new_resource.hostname, + :macaddress => new_resource.macaddress, + :ipaddress => new_resource.ipaddress, + :group => new_resource.group, + :options => new_resource.options + ) + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + utils_line "include \"#{filename}\";" do + action :add + file "/etc/dhcp3/hosts.d/host_list.conf" + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + if ::File.exists?(filename) + Chef::Log.debug "#{filename} created." + else + Chef::Log.error "#{filename} not created, #{new_resource.name} will loop in hardware-installing forever." + end +end + +action :remove do + filename = "/etc/dhcp3/hosts.d/#{new_resource.name}.conf" + if ::File.exists?(filename) + Chef::Log.info "Removing #{new_resource.name} host from /etc/dhcp3/hosts.d/" + file filename do + action :delete + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + new_resource.updated_by_last_action(true) + end + utils_line "include \"#{filename}\";" do + action :remove + file "/etc/dhcp3/hosts.d/host_list.conf" + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/subnet.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/subnet.rb new file mode 100644 index 00000000000..34256b689c7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/providers/subnet.rb @@ -0,0 +1,42 @@ + +action :add do + filename = "/etc/dhcp3/subnets.d/#{new_resource.subnet}.conf" + template filename do + cookbook "dhcp" + source "subnet.conf.erb" + variables( + :subnet => new_resource.subnet, + :netmask => new_resource.netmask, + :broadcast => new_resource.broadcast, + :routers => new_resource.routers, + :options => new_resource.options + ) + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + utils_line "include \"#{filename}\";" do + action :add + file "/etc/dhcp3/subnets.d/subnet_list.conf" + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end +end + +action :remove do + filename = "/etc/dhcp3/subnets.d/#{new_resource.name}.conf" + if ::File.exists?(filename) + Chef::Log.info "Removing #{new_resource.name} subnet from /etc/dhcp3/subnets.d/" + file filename do + action :delete + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end + new_resource.updated_by_last_action(true) + end + utils_line "include \"#{filename}\";" do + action :remove + file "/etc/dhcp3/subnets.d/subnet_list.conf" + notifies :restart, resources(:service => "dhcp3-server"), :delayed + end +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/recipes/default.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/recipes/default.rb new file mode 100644 index 00000000000..d59759d5288 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/recipes/default.rb @@ -0,0 +1,50 @@ + +include_recipe "utils" + +package "dhcp3-server" + +directory "/etc/dhcp3/groups.d" +directory "/etc/dhcp3/subnets.d" +directory "/etc/dhcp3/hosts.d" + +file "/etc/dhcp3/groups.d/group_list.conf" do + owner "root" + group "root" + mode 0644 +end +file "/etc/dhcp3/subnets.d/subnet_list.conf" do + owner "root" + group "root" + mode 0644 +end +file "/etc/dhcp3/hosts.d/host_list.conf" do + owner "root" + group "root" + mode 0644 +end + +template "/etc/dhcp3/dhcpd.conf" do + owner "root" + group "root" + mode 0644 + source "dhcpd.conf.erb" + variables(:options => node[:dhcp][:options]) +end + +# This needs to be evaled. +intfs = [ Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, "admin").interface ] + +template "/etc/default/dhcp3-server" do + owner "root" + group "root" + mode 0644 + source "dhcp3-server.erb" + variables(:interfaces => intfs) +end + +service "dhcp3-server" do + supports :restart => true, :status => true, :reload => true + enabled + action :nothing +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/group.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/group.rb new file mode 100644 index 00000000000..3f0d9430893 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/group.rb @@ -0,0 +1,6 @@ + +actions :add, :remove + +attribute :name, :kind_of => String, :name_attribute => true +attribute :options, :kind_of => Array, :default => [] + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/host.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/host.rb new file mode 100644 index 00000000000..e9faceba013 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/host.rb @@ -0,0 +1,10 @@ + +actions :add, :remove + +attribute :name, :kind_of => String, :name_attribute => true +attribute :hostname, :kind_of => String +attribute :macaddress, :kind_of => String +attribute :ipaddress, :kind_of => String +attribute :group, :kind_of => String +attribute :options, :kind_of => Array, :default => [] + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/subnet.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/subnet.rb new file mode 100644 index 00000000000..4d8409f65a6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/resources/subnet.rb @@ -0,0 +1,9 @@ + +actions :add, :remove + +attribute :subnet, :kind_of => String, :name_attribute => true +attribute :broadcast, :kind_of => String +attribute :netmask, :kind_of => String +attribute :routers, :kind_of => Array, :default => [] +attribute :options, :kind_of => Array, :default => [] + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/dhcp3-server.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/dhcp3-server.erb new file mode 100644 index 00000000000..e8ebc4ae0cc --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/dhcp3-server.erb @@ -0,0 +1,14 @@ +# Defaults for dhcp initscript +# sourced by /etc/init.d/dhcp +# installed at /etc/default/dhcp3-server by the maintainer scripts + +# +# This is a POSIX shell fragment +# + +# On what interfaces should the DHCP server (dhcpd) serve DHCP requests? +# Separate multiple interfaces with spaces, e.g. "sth0 sth1". +<% unless @interfaces.empty? -%> +INTERFACES="<%= @interfaces.collect! {|i| "#{i}" }.join(" ") %>" +<% end -%> + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/dhcpd.conf.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/dhcpd.conf.erb new file mode 100644 index 00000000000..70d3a9171a4 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/dhcpd.conf.erb @@ -0,0 +1,16 @@ +# File managed by chef + +<% if not @options.empty? -%> + <% @options.each do |option| -%> +<%= option %>; + <% end -%> +<% end -%> + +# Use this to send dhcp log messages to a different log file (you also +# have to hack syslog.conf to complete the redirection). +log-facility local7; + +include "/etc/dhcp3/groups.d/group_list.conf"; +include "/etc/dhcp3/subnets.d/subnet_list.conf"; +include "/etc/dhcp3/hosts.d/host_list.conf"; + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/group.conf.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/group.conf.erb new file mode 100644 index 00000000000..8b3d78a1e46 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/group.conf.erb @@ -0,0 +1,9 @@ +# File managed by puppet + +group "<%= @name %>" { +<% if not @options.empty? -%> +<% @options.each do |option| -%> + <%= option %>; +<% end -%> +<% end -%> +} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/host.conf.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/host.conf.erb new file mode 100644 index 00000000000..60afd0ee43c --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/host.conf.erb @@ -0,0 +1,11 @@ +host <%= @name %> { + option host-name "<%= @hostname %>"; + hardware ethernet <%= @macaddress %>; + fixed-address <%= @ipaddress %>; + group "<%= @group %>"; +<% if not @options.empty? -%> +<% @options.each do |option| -%> + <%= option %>; +<% end -%> +<% end -%> +} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/subnet.conf.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/subnet.conf.erb new file mode 100644 index 00000000000..d2b39d427c6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/dhcp/templates/default/subnet.conf.erb @@ -0,0 +1,14 @@ +# File managed by puppet + +subnet <%= @subnet %> netmask <%= @netmask%> { +<% if not @routers.empty? -%> + option routers <%= routers.collect! {|i| "#{i}" }.join(",") %>; +<% end -%> + option subnet-mask <%= @netmask %>; + option broadcast-address <%= @broadcast %>; +<% if not @options.empty? -%> +<% @options.each do |option| -%> + <%= option %>; +<% end -%> +<% end -%> +} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/metadata.json b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/metadata.json new file mode 100644 index 00000000000..b9cd23607e4 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "nfs": "Installs nfs" + }, + "maintainer_email": "crowbar@dell.com", + "name": "nfs", + "conflicting": { + }, + "description": "Installs nfs", + "long_description": "" + } diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/recipes/default.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/recipes/default.rb new file mode 100644 index 00000000000..d06b73bc3b5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/recipes/default.rb @@ -0,0 +1,43 @@ + +package "nfs-common" +package "portmap" +package "nfs-kernel-server" + +service "portmap" do + running true + enabled true + action [ :enable, :start ] +end + +link "/updates" do + to "/tftpboot/ubuntu_dvd/updates" +end + +directory "/install-logs" do + owner "root" + group "root" + mode 0755 +end + +service "nfs-kernel-server" do + supports :restart => true, :status => true, :reload => true + running true + enabled true + action [ :enable, :start ] +end + +template "/etc/exports" do + source "exports.erb" + group "root" + owner "root" + mode 0644 + variables(:admin_subnet => node["network"]["networks"]["admin"]["subnet"], + :admin_netmask => node["network"]["networks"]["admin"]["netmask"]) + notifies :run, "execute[nfs-export]", :delayed +end + +execute "nfs-export" do + command "exportfs -a" + action :run +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/templates/default/exports.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/templates/default/exports.erb new file mode 100644 index 00000000000..280ccaec7ba --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/nfs-server/templates/default/exports.erb @@ -0,0 +1,3 @@ +/install-logs <%= @admin_subnet %>/<%= @admin_netmask %>(rw,async,no_root_squash,no_subtree_check) +/updates <%= @admin_subnet %>/<%= @admin_netmask %>(ro,sync,no_subtree_check) + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/files/default/tftpd-hpa b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/files/default/tftpd-hpa new file mode 100644 index 00000000000..56bcace4c74 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/files/default/tftpd-hpa @@ -0,0 +1,8 @@ + +# Puppet managed, do not edit. + +TFTP_USERNAME="tftp" +TFTP_DIRECTORY="/tftpboot" +TFTP_ADDRESS="0.0.0.0:69" +TFTP_OPTIONS="--secure" + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/metadata.json b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/metadata.json new file mode 100644 index 00000000000..0e661ccbe58 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "tftpd": "Installs the tftpd-hpa server" + }, + "maintainer_email": "crowbar@dell.com", + "name": "tftpd", + "conflicting": { + }, + "description": "Installs the tftpd-hpa server", + "long_description": "" + } diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/recipes/default.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/recipes/default.rb new file mode 100644 index 00000000000..539b4f74dfe --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/tftpd/recipes/default.rb @@ -0,0 +1,18 @@ + +package "tftpd-hpa" + +cookbook_file "/etc/default/tftpd-hpa" do + owner "root" + group "root" + mode 0644 + source "tftpd-hpa" + notifies :restart, "service[tftpd-hpa]" +end + +service "tftpd-hpa" do + supports :restart => true, :status => true, :reload => true + running true + enabled true + action [ :enable, :start ] +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/README.rdoc b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/README.rdoc new file mode 100644 index 00000000000..bddd59c4be5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/README.rdoc @@ -0,0 +1,51 @@ += DESCRIPTION: + + += REQUIREMENTS: + +== Platform: + + +== Cookbooks: + + +* ruby +* apache2 +* passenger + += ATTRIBUTES: + +* rails[:version] - Install the specified version. Default false (installs latest). +* rails[:environment] - Set Rails environment. Default production. + += USAGE: + +The recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define. + + include_recipe "apache2" + include_recipe "passenger" + include_recipe "rails" + + web_app "some_rails_app" do + docroot "/srv/some_rails_app/public" + template "some_rails_app.conf.erb" + end + +We provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/attributes/default.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/attributes/default.rb new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/attributes/default.rb @@ -0,0 +1 @@ + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/apt.conf b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/apt.conf new file mode 100644 index 00000000000..8602f01c0d5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/apt.conf @@ -0,0 +1 @@ +APT::Get::AllowUnauthenticated 1 ; diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/authorized_keys b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/authorized_keys new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/chef-client b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/chef-client new file mode 100644 index 00000000000..ddbb7818144 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/chef-client @@ -0,0 +1,4 @@ +LOGFILE=/var/log/chef/client.log +CONFIG=/etc/chef/client.rb +INTERVAL=900 +SPLAY=20 diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/localboot.default b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/localboot.default new file mode 100644 index 00000000000..b59736f6544 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/localboot.default @@ -0,0 +1,8 @@ +include ../../isolinux/menu.cfg +default localboot +prompt 0 +timeout 10 + +LABEL localboot + menu label ^Boot from first hard disk + localboot 0x80 diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/net-post-install.sh b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/net-post-install.sh new file mode 100755 index 00000000000..5d844230809 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/net-post-install.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# This script is called by the net_ubuntu.seed file during +# ubuntu install. It handles doing the manual sets needed to layout the +# filesystem the rest of the way before running the common_install.sh. +# +# The intent is that once the system is finished installing, the customer +# will edit the config.json file and run install.sh to complete installation +# and initial configuration. +# + +# +# We are a net install. +# + +HTTP_SERVER=$1 + +# Example +#mkdir -p /target/tftpboot/ubuntu_dvd/dell +#wget http://$HTTP_SERVER/ubuntu_dvd/dell/common_install.sh -O /target/tftpboot/ubuntu_dvd/dell/common_install.sh +#wget http://$HTTP_SERVER/ubuntu_dvd/dvd.tgz -O /target/tftpboot/ubuntu_dvd/dvd.tgz +#wget http://$HTTP_SERVER/ubuntu_dvd/jill_ssh.tgz -O /target/tftpboot/ubuntu_dvd/jill_ssh.tgz +# +#chmod +x /target/tftpboot/ubuntu_dvd/dell/common_install.sh +# +#if [ "$2" != "$3" ] +#then +# chroot /target bash /tftpboot/ubuntu_dvd/dell/common_install.sh fix_if +#else +# chroot /target bash /tftpboot/ubuntu_dvd/dell/common_install.sh +#fi + +rsyslog_dir="/target/etc/rsyslog.d" +if [ -d "$rsyslog_dir" ]; then + if [ ! -f "$rsyslog_dir/10-crowbar-client.conf" ]; then + echo "*.* @@${HTTP_SERVER%:*}" > "$rsyslog_dir/10-crowbar-client.conf" + if ! wget -O "$rsyslog_dir/00-crowbar-debug.conf" -q http://$HTTP_SERVER/ubuntu_dvd/rsyslog.d/00-crowbar-debug.conf + then + rm -f "$rsyslog_dir/00-crowbar-debug.conf" + fi + fi +fi +mkdir -p /target/root/.ssh +chmod 700 /target/root/.ssh +if ! wget -O /target/root/.ssh/authorized_keys.wget -q http://$HTTP_SERVER/ubuntu_dvd/authorized_keys; then + rm -f /target/root/.ssh/authorized_keys.wget +else + chmod 644 /target/root/.ssh/authorized_keys + cat /target/root/.ssh/authorized_keys.wget >>/target/root/.ssh/authorized_keys + rm -f /target/root/.ssh/authorized_keys.wget +fi +sync +sync + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/net-pre-install.sh b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/net-pre-install.sh new file mode 100755 index 00000000000..e8838fb875f --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/net-pre-install.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# This script is called by the net_ubuntu.seed file during +# ubuntu install. It handles doing the manual sets needed to layout the +# filesystem the rest of the way before running the common_install.sh. +# +# The intent is that once the system is finished installing, the customer +# will edit the config.json file and run install.sh to complete installation +# and initial configuration. +# + +# +# We are a net install. +# + +HTTP_SERVER=$1 + +IP=${HTTP_SERVER%%:*} +HOSTNAME=$(grep host-name /var/lib/dhcp3/dhclient.leases| \ + cut -d'"' -f2 | head -1) + +export CROWBAR_KEY="$(grep -o 'crowbar\.install\.key=[^ ]*' /proc/cmdline)" +if [ -z "$CROWBAR_KEY" ]; then + echo "Cannot find install key, this will not end well." +fi + +post_state() { + echo "{" > /tmp/post_state.$$ + echo " \"name\": \"$1\"," >> /tmp/post_state.$$ + echo " \"state\": \"$2\"" >> /tmp/post_state.$$ + echo "}" >> /tmp/post_state.$$ + curl -o - --connect-timeout 60 -s -u "$CROWBAR_KEY" \ + --digest -L --data-binary @/tmp/post_state.$$ -X POST \ + -H "Accept: application/json" -H "Content-Type: application/json" \ + "http://$IP:3000/crowbar/crowbar/1.0/transition/default" + rm /tmp/post_state.$$ +} + +post_state $HOSTNAME "installing" diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/nova_install.sh b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/nova_install.sh new file mode 100755 index 00000000000..95e0969ce35 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/files/default/nova_install.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# This script is called by the net_ubuntu.seed file during +# ubuntu install. It handles doing the manual sets needed to layout the +# filesystem the rest of the way before running the common_install.sh. +# +# The intent is that once the system is finished installing, the customer +# will edit the config.json file and run install.sh to complete installation +# and initial configuration. +# + +# +# We are a net install. +# + +HTTP_SERVER=$1 +IP=${HTTP_SERVER%%:*} + +echo "deb http://$HTTP_SERVER/ubuntu_dvd maverick main restricted" > /target/etc/apt/sources.list +echo "deb http://$HTTP_SERVER/ubuntu_dvd/extra /" >> /target/etc/apt/sources.list + +rsyslog_dir="/target/etc/rsyslog.d" +if [ -d "$rsyslog_dir" ]; then + if [ ! -f "$rsyslog_dir/10-crowbar-client.conf" ]; then + echo "*.* @@${IP}" > "$rsyslog_dir/10-crowbar-client.conf" + if ! wget -O "$rsyslog_dir/00-crowbar-debug.conf" -q http://$HTTP_SERVER/ubuntu_dvd/rsyslog.d/00-crowbar-debug.conf + then + rm -f "$rsyslog_dir/00-crowbar-debug.conf" + fi + fi +fi + +mkdir -p /target/root/.ssh +chmod 700 /target/root/.ssh +if ! wget -O /target/root/.ssh/authorized_keys.wget -q http://$HTTP_SERVER/ubuntu_dvd/authorized_keys; then + rm -f /target/root/.ssh/authorized_keys.wget +else + chmod 644 /target/root/.ssh/authorized_keys + cat /target/root/.ssh/authorized_keys.wget >>/target/root/.ssh/authorized_keys + rm -f /target/root/.ssh/authorized_keys.wget +fi + +wget -q http://$HTTP_SERVER/ubuntu_dvd/nova_install/crowbar_join.sh -O- > /target/etc/init.d/crowbar_join.sh + +sed "s/@@IP/$IP/g" > /target/update_system2.sh <<"EOF" +#!/bin/bash + +key_re='crowbar\.install\.key=([^ ]+)' +if [[ $(cat /proc/cmdline) =~ $key_re ]]; then + export CROWBAR_KEY="${BASH_REMATCH[1]}" + echo "$CROWBAR_KEY" >/etc/crowbar.install.key +elif [[ -f /etc/crowbar.install.key ]]; then + export CROWBAR_KEY="$(cat /etc/crowbar.install.key)" +fi + +post_state() { + local curlargs=(-o "/var/log/$1-$2.json" --connect-timeout 60 -s \ + -L -X POST --data-binary "{ \"name\": \"$1\", \"state\": \"$2\" }" \ + -H "Accept: application/json" -H "Content-Type: application/json" \ + --max-time 240) + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) + curl "${curlargs[@]}" "http://@@IP:3000/crowbar/crowbar/1.0/transition/default" +} + +HOSTNAME=$(hostname -f) + +post_state $HOSTNAME "installing" + +sleep 30 + +post_state $HOSTNAME "installed" + +# Wait for DHCP to update - this is mainly for virtual environments or really large deploys +sleep 30 + +EOF + +mount /proc /target/proc +chmod +x /target/etc/init.d/crowbar_join.sh +chmod +x /target/update_system2.sh +chroot /target sed -i "s/__HTTP_SERVER__/$HTTP_SERVER/" /etc/init.d/crowbar_join.sh +chroot /target ln -s /etc/init.d/crowbar_join.sh /etc/rc3.d/S80crowbar +chroot /target ln -s /etc/init.d/crowbar_join.sh /etc/rc5.d/S80crowbar +chroot /target ln -s /etc/init.d/crowbar_join.sh /etc/rc2.d/S80crowbar +chroot /target /update_system2.sh +sync diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/metadata.json b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/metadata.json new file mode 100644 index 00000000000..8e7a43db0a6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "ubuntu-install": "Installs ubuntu install directories" + }, + "maintainer_email": "crowbar@dell.com", + "name": "ubuntu-install", + "conflicting": { + }, + "description": "Installs ubuntu install directories", + "long_description": "" +} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/apt_repo.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/apt_repo.rb new file mode 100644 index 00000000000..cbd371101b9 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/apt_repo.rb @@ -0,0 +1,11 @@ + +c = cookbook_file "/etc/apt/apt.conf" do + source "apt.conf" + owner "root" + group "root" + mode "0644" + action :create +end + +c.run_action(:create) + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/base.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/base.rb new file mode 100644 index 00000000000..5f043a2288e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/base.rb @@ -0,0 +1,24 @@ + +directory "/root/.ssh" do + owner "root" + group "root" + mode "0700" + action :create +end + +cookbook_file "/root/.ssh/authorized_keys" do + owner "root" + group "root" + mode "0700" + action :create + source "authorized_keys" +end + +cookbook_file "/etc/default/chef-client" do + owner "root" + group "root" + mode "0644" + action :create + source "chef-client" +end + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/default.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/default.rb new file mode 100644 index 00000000000..4626d8584a8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/recipes/default.rb @@ -0,0 +1,210 @@ + +web_port = node[:provisioner][:web_port] +use_local_security = node[:provisioner][:use_local_security] +admin_ip = Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, "admin").address +admin_net = node[:network][:networks]["admin"] + +lease_time = node[:provisioner][:dhcp]["lease-time"] +domain_name = node[:dns].nil? ? node[:domain] : (node[:dns][:domain] || node[:domain]) +dhcp_start = admin_net[:ranges]["dhcp"]["start"] +dhcp_end = admin_net[:ranges]["dhcp"]["end"] +machine_install_key = ::File.read("/etc/crowbar.install.key").chomp.strip + +serial_console = node[:provisioner][:use_serial_console] ? "console=tty0 console=ttyS1,115200n8" : "" + +# +# Setup links from centos image to our other two names +# +["update", "hwinstall"].each do |dir| + link "/tftpboot/ubuntu_dvd/#{dir}" do + action :create + to "/tftpboot/ubuntu_dvd/discovery" + not_if "test -L /tftpboot/ubuntu_dvd/#{dir}" + end +end + +# +# Update the kernel lines and default configs for our +# three boot environments (centos images are updated +# with discovery). +# +["nova_install", "execute", "discovery"].each do |image| + install_path = "/tftpboot/ubuntu_dvd/#{image}" + + # Make sure the directories need to net_install are there. + directory "#{install_path}" + directory "#{install_path}/pxelinux.cfg" + + # Everyone needs a pxelinux.0 + link "#{install_path}/pxelinux.0" do + action :create + to "../isolinux/pxelinux.0" + not_if "test -L #{install_path}/pxelinux.0" + end + + case + when image == "discovery" + template "#{install_path}/pxelinux.cfg/default" do + mode 0644 + owner "root" + group "root" + source "default.erb" + variables(:append_line => "append initrd=initrd0.img #{serial_console} root=/sledgehammer.iso rootfstype=iso9660 rootflags=loop crowbar.install.key=#{machine_install_key}", + :install_name => image, + :kernel => "vmlinuz0") + end + next + + when image == "execute" + cookbook_file "#{install_path}/pxelinux.cfg/default" do + mode 0644 + owner "root" + group "root" + source "localboot.default" + end + next + # The nova_install case + else + append_line="append crowbar.install.key=#{machine_install_key} #{serial_console} url=http://#{admin_ip}:#{web_port}/ubuntu_dvd/#{image}/net_seed debian-installer/locale=en_US.utf8 console-setup/layoutcode=us localechooser/translation/warn-light=true localechooser/translation/warn-severe=true netcfg/choose_interface=auto netcfg/get_hostname=\"redundant\" initrd=../install/netboot/ubuntu-installer/amd64/initrd.gz ramdisk_size=16384 root=/dev/ram rw quiet --" + + template "#{install_path}/pxelinux.cfg/default" do + mode 0644 + owner "root" + group "root" + source "default.erb" + variables(:append_line => append_line, + :install_name => image, + :kernel => "../install/netboot/ubuntu-installer/amd64/linux") + end + + template "#{install_path}/net_seed" do + mode 0644 + owner "root" + group "root" + source "net_seed.erb" + variables(:install_name => image, + :cc_use_local_security => use_local_security, + :cc_install_web_port => web_port, + :cc_built_admin_node_ip => admin_ip) + end + + cookbook_file "#{install_path}/net-post-install.sh" do + mode 0644 + owner "root" + group "root" + source "#{image}.sh" + end + + cookbook_file "#{install_path}/net-pre-install.sh" do + mode 0644 + owner "root" + group "root" + source "net-pre-install.sh" + end + + template "#{install_path}/crowbar_join.sh" do + mode 0644 + owner "root" + group "root" + source "crowbar_join.sh.erb" + variables(:admin_ip => admin_ip) + end + end + +end + +web_app "install_app" do + server_name node[:fqdn] + docroot "/tftpboot" + template "install_app.conf.erb" +end + +bash "copy validation pem" do + code "cp /etc/chef/validation.pem /tftpboot/ubuntu_dvd" + not_if "test -f /tftpboot/ubuntu_dvd/validation.pem" +end + +file "/tftpboot/ubuntu_dvd/validation.pem" do + mode 0444 +end + +dhcp_groups = {"nova_install" => 2, "hwinstall" => 1, "update" => 3, "execute" => 4} +dhcp_groups.each do |group, dhcp_state| + dhcp_group group do + action :add + options [ "option domain-name \"#{domain_name}\"", + "option dhcp-client-state #{dhcp_state}", + "filename \"/ubuntu_dvd/#{group}/pxelinux.0\"" ] + end +end + +dhcp_subnet admin_net["subnet"] do + action :add + broadcast admin_net["broadcast"] + netmask admin_net["netmask"] + options [ "option domain-name \"#{domain_name}\"", + "option domain-name-servers #{admin_ip}", + "option routers #{admin_net["router"]}", + "range #{dhcp_start} #{dhcp_end}", + "default-lease-time #{lease_time}", + "max-lease-time #{lease_time}", + 'filename "/ubuntu_dvd/discovery/pxelinux.0"' ] +end + +states = node["provisioner"]["dhcp"]["state_machine"] +nodes = search(:node, "crowbar_usedhcp:true") +if not nodes.nil? and not nodes.empty? + nodes.each do |mnode| + next if mnode[:state].nil? + + new_group = nil + newstate = states[mnode[:state]] + new_group = newstate if !newstate.nil? && newstate != "noop" + + next if new_group.nil? + + admin_data_net = Chef::Recipe::Barclamp::Inventory.get_network_by_type(mnode, "admin") + + # Skip if we don't have admin + next if admin_data_net.nil? + + mac_list = [] + mnode["network"]["interfaces"].each do |net, net_data| + net_data.each do |field, field_data| + next if field != "addresses" + + field_data.each do |addr, addr_data| + next if addr_data["family"] != "lladdr" + mac_list << addr unless mac_list.include? addr + end + end + end + + # Build entries for each mac address. + count = 0 + mac_list.each do |mac| + count = count+1 + if new_group == "reset" or new_group == "delete" + dhcp_host "#{mnode.name}-#{count}" do + hostname mnode.name + ipaddress admin_data_net.address + macaddress mac + group new_group + action :remove + end + + system("knife node delete -y #{mnode.name} -u chef-webui -k /etc/chef/webui.pem") if new_group == "delete" + else + dhcp_host "#{mnode.name}-#{count}" do + hostname mnode.name + ipaddress admin_data_net.address + macaddress mac + group new_group + action :add + end + end + end + end +end + + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/crowbar_join.sh.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/crowbar_join.sh.erb new file mode 100644 index 00000000000..da254873485 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/crowbar_join.sh.erb @@ -0,0 +1,153 @@ +#!/bin/bash + +HTTP_SERVER="<%= @admin_ip %>:8091" +IP=${HTTP_SERVER%:*} + +exec 2>>/var/log/crowbar-join.errlog + +export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): ' +set -x + +if [[ -f /etc/crowbar.install.key ]]; then + export CROWBAR_KEY="$(cat /etc/crowbar.install.key)" +fi + +# Run a command and log its output. +log_to() { + # $1 = install log to log to + # $@ = rest of args + local __log="/var/log/crowbar-join-$1" + local __timestamp="$(date '+%F %T %z')" + shift + printf "\n%s\n" "$__timestamp: Running $*" | \ + tee -a "$__log.err" >> "$__log.log" + local _ret=0 + if "$@" 2>> "$__log.err" >>"$__log.log"; then + _ret=0 + else + _ret="$?" + echo "$__timestamp: $* failed." + echo "See $__log.log and $__log.err for more information." + fi + printf "\n$s\n--------\n" "$(date '+%F %T %z'): Done $*" | \ + tee -a "$__log.err" >> "$__log.log" + return $_ret +} + +post_state() { + local curlargs=(-o "/var/log/$1-$2.json" --connect-timeout 60 -s \ + -L -X POST --data-binary "{ \"name\": \"$1\", \"state\": \"$2\" }" \ + -H "Accept: application/json" -H "Content-Type: application/json" \ + --max-time 240) + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) + curl "${curlargs[@]}" "http://$IP:3000/crowbar/crowbar/1.0/transition/default" +} + +# Spin while we wait for the interface to come up. + +echo "Waiting on our network interface to come up..." +while ! ip addr | grep -v " lo" | grep -q "inet " +do + sleep 1 +done + +# Get our hostname +HOSTNAME=$(hostname -f) + +sync_time() { + # stop ntpd before we run ntpdate, and start it again afterwards. + service ntp stop + killall ntpd + while ! /usr/sbin/ntpdate -b $IP; do + echo "Waiting for NTP server" + sleep 1 + done +} + +# Clean up install droppings +rm -f /update_system2.sh +rm -f /net-post-install.sh + +echo "Synchronizing time (pass 1)" +sync_time + +# Mark us as readying, and get our cert. +post_state $HOSTNAME "readying" +final_state="ready" +mkdir -p /etc/chef +wget -q "http://$HTTP_SERVER/ubuntu_dvd/validation.pem" -O- > /etc/chef/validation.pem + +# Install Chef +echo "Installing Chef..." +echo "chef chef/chef_server_url string http://$IP:4000" >/tmp/debsel.conf + +log_to apt /usr/bin/apt-get update +log_to apt /usr/bin/debconf-set-selections /tmp/debsel.conf +log_to apt /usr/bin/apt-get --force-yes -y install chef + +log_to chef service chef-client stop +log_to chef killall chef-client + +# Run Chef +echo "Syncing time (pass 2)" +sync_time + +# Until we arrange for the network to transisiton from using +# DHCP somewhere else, the first run of chef-client will always die due to +# the networking barclamp changing the IP address from dhcp to static. +# We will try to pick up and run with it. +echo "Running Chef Client (pass 1)" +log_to chef chef-client -l debug -V + +# Make sure our interfaces are as up as we can get them +echo "Ensuring that our network interfaces are up." +log_to ifup ifup -a + +# Only transition to problem state if the second run fails. +echo "Running Chef Client (pass 2)" +if ! log_to chef chef-client -l debug -V; then + log_to ifup ifup -a + post_state $HOSTNAME "problem" + echo "Error Path" + echo "Syncing Time (pass 3)" + sync_time + echo "Removing Chef Cache" + rm -rf /var/cache/chef/* + echo "Checking Install Integrity" + log_to apt /usr/bin/apt-get -q --force-yes -y install + echo "Running Chef Client (pass 3) - apt/cache cleanup" + if ! log_to chef chef-client -l debug -V; then + log_to ifup ifup -a + post_state $HOSTNAME "problem" + echo "Error Path" + echo "Syncing Time (pass 4)" + sync_time + echo "Removing Chef Cache" + rm -rf /var/cache/chef/* + echo "Checking Install Integrity" + log_to apt /usr/bin/apt-get -q --force-yes -y install + echo "Checking Keys" + rm -f /etc/chef/client.pem + post_state $HOSTNAME "hardware-updated" + post_state $HOSTNAME "problem" + echo "Running Chef Client (pass 4) - password cleanup" + if ! log_to chef chef-client -l debug -V; then + log_to ifup ifup -a + echo "chef-client run failed four times, giving up." + echo "Failed" + printf "Our IP address is: %s\n" "$(ip addr show dev eth0)" + final_state="problem" + fi + fi +fi + +# Make sure our code is up to date +log_to apt apt-get -q --force-yes -y upgrade + +# Transition to our final state +post_state $HOSTNAME "$final_state" + +log_to time service ntp start +log_to chef service chef-client start + +echo "Done" diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/default.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/default.erb new file mode 100644 index 00000000000..e8ef46368c5 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/default.erb @@ -0,0 +1,7 @@ +DEFAULT <%= @install_name %> +PROMPT 0 +TIMEOUT 10 +LABEL <%= @install_name %> + KERNEL <%= @kernel %> + <%= @append_line %> + IPAPPEND 2 diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/install_app.conf.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/install_app.conf.erb new file mode 100644 index 00000000000..6b896861346 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/install_app.conf.erb @@ -0,0 +1,14 @@ +Listen 8091 + + ServerName <%= @params[:server_name] %> + DocumentRoot <%= @params[:docroot] %> + + LogLevel warn + ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>_error.log + CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>_access.log combined + + + Options FollowSymLinks +Indexes + AllowOverride None + + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/net_seed.erb b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/net_seed.erb new file mode 100644 index 00000000000..839f913b6a2 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/net_seed.erb @@ -0,0 +1,393 @@ +#### Contents of the preconfiguration file (for &releasename;) +### Localization +# Locale sets language and country. +d-i debian-installer/locale string en_US.utf8 + +# Keyboard selection. +# Disable automatic (interactive) keymap detection. +d-i console-setup/ask_detect boolean false +#d-i console-setup/modelcode string pc105 +d-i console-setup/layoutcode string us +# To select a variant of the selected layout (if you leave this out, the +# basic form of the layout will be used): +#d-i console-setup/variantcode string dvorak + +### Network configuration +# netcfg will choose an interface that has link if possible. This makes it +# skip displaying a list if there is more than one interface. +d-i netcfg/choose_interface select auto + +# To pick a particular interface instead: +#d-i netcfg/choose_interface select eth1 + +# If you have a slow dhcp server and the installer times out waiting for +# it, this might be useful. +d-i netcfg/dhcp_timeout string 120 + +# If you prefer to configure the network manually, uncomment this line and +# the static network configuration below. +#d-i netcfg/disable_dhcp boolean true + +# If you want the preconfiguration file to work on systems both with and +# without a dhcp server, uncomment these lines and the static network +# configuration below. +#d-i netcfg/dhcp_failed note +#d-i netcfg/dhcp_options select Configure network manually + +# Static network configuration. +#d-i netcfg/get_nameservers string +#d-i netcfg/get_ipaddress string +#d-i netcfg/get_netmask string +#d-i netcfg/get_gateway string +#d-i netcfg/confirm_static boolean true + +# Any hostname and domain names assigned from dhcp take precedence over +# values set here. However, setting the values still prevents the questions +# from being shown, even if values come from dhcp. +d-i netcfg/get_hostname string unassigned-hostname +d-i netcfg/get_domain string unassigned-domain + +# Disable that annoying WEP key dialog. +#d-i netcfg/wireless_wep string +# The wacky dhcp hostname that some ISPs use as a password of sorts. +#d-i netcfg/dhcp_hostname string radish + +# If non-free firmware is needed for the network or other hardware, you can +# configure the installer to always try to load it, without prompting. Or +# change to false to disable asking. +#d-i hw-detect/load_firmware boolean true + +### Mirror settings +# If you select ftp, the mirror/country string does not need to be set. +#d-i mirror/protocol string ftp +#d-i mirror/country string manual +#d-i mirror/http/hostname string http.us.debian.org +#d-i mirror/http/directory string /ubuntu +#d-i mirror/http/proxy string +# If you select ftp, the mirror/country string does not need to be set. +d-i mirror/country string manual +d-i mirror/protocol string http +d-i mirror/http/hostname string <%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %> +d-i mirror/http/directory string /ubuntu_dvd +d-i mirror/http/proxy string + +# Alternatively: by default, the installer uses CC.archive.ubuntu.com where +# CC is the ISO-3166-2 code for the selected country. You can preseed this +# so that it does so without asking. +#d-i mirror/http/mirror select CC.archive.ubuntu.com + +# Suite to install. +#d-i mirror/suite string &releasename; +# Suite to use for loading installer components (optional). +#d-i mirror/udeb/suite string &releasename; +# Components to use for loading installer components (optional). +#d-i mirror/udeb/components multiselect main, restricted + +### Clock and time zone setup +# Controls whether or not the hardware clock is set to UTC. +d-i clock-setup/utc boolean true + +# You may set this to any valid setting for $TZ; see the contents of +# /usr/share/zoneinfo/ for valid values. +d-i time/zone string US/Central + +# Controls whether to use NTP to set the clock during the install +d-i clock-setup/ntp boolean false +# NTP server to use. The default is almost always fine here. +#d-i clock-setup/ntp-server string ntp.example.com + +### Partitioning +# If the system has free space you can choose to only partition that space. +# Alternatives: custom, some_device, some_device_crypto, some_device_lvm. +#d-i partman-auto/init_automatically_partition select biggest_free + +# Alternatively, you can specify a disk to partition. The device name must +# be given in traditional non-devfs format. +# Note: A disk must be specified, unless the system has only one disk. +# For example, to use the first SCSI/SATA hard disk: +d-i partman-auto/disk string /dev/sda +# In addition, you'll need to specify the method to use. +# The presently available methods are: "regular", "lvm" and "crypto" +d-i partman-auto/method string lvm + +# If one of the disks that are going to be automatically partitioned +# contains an old LVM configuration, the user will normally receive a +# warning. This can be preseeded away... +d-i partman-lvm/device_remove_lvm boolean true +# The same applies to pre-existing software RAID array: +d-i partman-md/device_remove_md boolean true +# And the same goes for the confirmation to write the lvm partitions. +d-i partman-lvm/confirm boolean true +d-i partman-lvm/confirm_nochanges boolean true +d-i partman-lvm/confirm_nooverwrite boolean true + +# For LVM partitioning, you can select how much of the volume group to use +# for logical volumes. +d-i partman-auto-lvm/guided_size string max +#d-i partman-auto-lvm/guided_size string 10GB +#d-i partman-auto-lvm/guided_size string 50% + +# You can choose one of the three predefined partitioning recipes: +# - atomic: all files in one partition +# - home: separate /home partition +# - multi: separate /home, /usr, /var, and /tmp partitions +d-i partman-auto/choose_recipe select atomic + +# Or provide a recipe of your own... +# The recipe format is documented in the file devel/partman-auto-recipe.txt. +# If you have a way to get a recipe file into the d-i environment, you can +# just point at it. +#d-i partman-auto/expert_recipe_file string /hd-media/recipe + +# If not, you can put an entire recipe into the preconfiguration file in one +# (logical) line. This example creates a small /boot partition, suitable +# swap, and uses the rest of the space for the root partition: +#d-i partman-auto/expert_recipe string \ +# boot-root :: \ +# 40 50 100 ext3 \ +# $primary{ } $bootable{ } \ +# method{ format } format{ } \ +# use_filesystem{ } filesystem{ ext3 } \ +# mountpoint{ /boot } \ +# . \ +# 500 10000 1000000000 ext3 \ +# method{ format } format{ } \ +# use_filesystem{ } filesystem{ ext3 } \ +# mountpoint{ / } \ +# . \ +# 64 512 300% linux-swap \ +# method{ swap } format{ } \ +# . + +# If you just want to change the default filesystem from ext3 to something +# else, you can do that without providing a full recipe. +#d-i partman/default_filesystem string ext4 + +# This makes partman automatically partition without confirmation, provided +# that you told it what to do using one of the methods above. +d-i partman/confirm_write_new_label boolean true +d-i partman/choose_partition select finish +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true + +### Controlling how partitions are mounted +# The default is to mount by UUID, but you can also choose "traditional" to +# use traditional device names, or "label" to try filesystem labels before +# falling back to UUIDs. +#d-i partman/mount_style select uuid + +### Base system installation +# The kernel image (meta) package to be installed; "none" can be used if no +# kernel is to be installed. +#d-i base-installer/kernel/image string linux-generic + +### Account setup +# Skip creation of a root account (normal user account will be able to +# use sudo). The default is false; preseed this to true if you want to set +# a root password. +#d-i passwd/root-login boolean false +# Alternatively, to skip creation of a normal user account. +#d-i passwd/make-user boolean false + +# Root password, either in clear text +#d-i passwd/root-password password r00tme +#d-i passwd/root-password-again password r00tme +# or encrypted using an MD5 hash. +#d-i passwd/root-password-crypted password [MD5 hash] + +# To create a normal user account. +d-i passwd/user-fullname string <%= node[:provisioner][:default_user] %> +d-i passwd/username string <%= node[:provisioner][:default_user] %> +<% if node[:provisioner][:default_password_hash].nil? -%> +# Normal user's password, either in clear text +d-i passwd/user-password password <%= node[:provisioner][:default_password] %> +d-i passwd/user-password-again password <%= node[:provisioner][:default_password] %> +<% else -%> +# or encrypted using an MD5 hash. +d-i passwd/user-password-crypted password <%= node[:provisioner][:default_password_hash] %> +<% end -%> +# Create the first user with the specified UID instead of the default. +#d-i passwd/user-uid string 1010 +# The installer will warn about weak passwords. If you are sure you know +# what you're doing and want to override it, uncomment this. +d-i user-setup/allow-password-weak boolean true + +# The user account will be added to some standard initial groups. To +# override that, use this. +#d-i passwd/user-default-groups string audio cdrom video + +# Set to true if you want to encrypt the first user's home directory. +d-i user-setup/encrypt-home boolean false + +### Apt setup +# You can choose to install restricted and universe software, or to install +# software from the backports repository. +#d-i apt-setup/restricted boolean true +#d-i apt-setup/universe boolean true +#d-i apt-setup/backports boolean true +# Uncomment this if you don't want to use a network mirror. +#d-i apt-setup/use_mirror boolean false +# Select which update services to use; define the mirrors to be used. +# Values shown below are the normal defaults. +#d-i apt-setup/services-select multiselect security +#d-i apt-setup/security_host string security.ubuntu.com +#d-i apt-setup/security_path string /ubuntu +<% if @cc_use_local_security -%> +d-i apt-setup/security_host string <%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %> +d-i apt-setup/security_path string /ubuntu_dvd +<% end -%> + +# Additional repositories, local[0-9] available +#d-i apt-setup/local0/repository string \ +# http://local.server/ubuntu &releasename; main +#d-i apt-setup/local0/comment string local server +# Enable deb-src lines +#d-i apt-setup/local0/source boolean true +# URL to the public key of the local repository; you must provide a key or +# apt will complain about the unauthenticated repository and so the +# sources.list line will be left commented out +#d-i apt-setup/local0/key string http://local.server/key + +# By default the installer requires that repositories be authenticated +# using a known gpg key. This setting can be used to disable that +# authentication. Warning: Insecure, not recommended. +#d-i debian-installer/allow_unauthenticated string true + +### Package selection +#tasksel tasksel/first multiselect ubuntu-desktop +tasksel tasksel/first multiselect ubuntu-standard, openssh-server +#tasksel tasksel/first multiselect kubuntu-desktop + +# Individual additional packages to install +d-i pkgsel/include string openssh-server curl +# Whether to upgrade packages after debootstrap. +# Allowed values: none, safe-upgrade, full-upgrade +#d-i pkgsel/upgrade select none + +# Language pack selection +#d-i pkgsel/language-packs multiselect de, en, zh + +# Policy for applying updates. May be "none" (no automatic updates), +# "unattended-upgrades" (install security updates automatically), or +# "landscape" (manage system with Landscape). +d-i pkgsel/update-policy select none + +# Some versions of the installer can report back on what software you have +# installed, and what software you use. The default is not to report back, +# but sending reports helps the project determine what software is most +# popular and include it on CDs. +#popularity-contest popularity-contest/participate boolean false + +# By default, the system's locate database will be updated after the +# installer has finished installing most packages. This may take a while, so +# if you don't want it, you can set this to "false" to turn it off. +#d-i pkgsel/updatedb boolean true + +### Boot loader installation +# Grub is the default boot loader (for x86). If you want lilo installed +# instead, uncomment this: +#d-i grub-installer/skip boolean true +# To also skip installing lilo, and install no bootloader, uncomment this +# too: +#d-i lilo-installer/skip boolean true + +# This is fairly safe to set, it makes grub install automatically to the MBR +# if no other operating system is detected on the machine. +d-i grub-installer/only_debian boolean true + +# This one makes grub-installer install to the MBR if it also finds some other +# OS, which is less safe as it might not be able to boot that other OS. +#d-i grub-installer/with_other_os boolean true + +# Alternatively, if you want to install to a location other than the mbr, +# uncomment and edit these lines: +#d-i grub-installer/only_debian boolean false +#d-i grub-installer/with_other_os boolean false +#d-i grub-installer/bootdev string (hd0,0) +# To install grub to multiple disks: +#d-i grub-installer/bootdev string (hd0,0) (hd1,0) (hd2,0) + +# Optional password for grub, either in clear text +#d-i grub-installer/password password r00tme +#d-i grub-installer/password-again password r00tme +# or encrypted using an MD5 hash, see grub-md5-crypt(8). +#d-i grub-installer/password-crypted password [MD5 hash] + +### Finishing up the installation +# During installations from serial console, the regular virtual consoles +# (VT1-VT6) are normally disabled in /etc/inittab. Uncomment the next +# line to prevent this. +#d-i finish-install/keep-consoles boolean true + +# Avoid that last message about the install being complete. +d-i finish-install/reboot_in_progress note + +# This will prevent the installer from ejecting the CD during the reboot, +# which is useful in some situations. +#d-i cdrom-detect/eject boolean false + +# This is how to make the installer shutdown when finished, but not +# reboot into the installed system. +#d-i debian-installer/exit/halt boolean true +# This will power off the machine instead of just halting it. +#d-i debian-installer/exit/poweroff boolean true + +### X configuration +# X can detect the right driver for some cards, but if you're preseeding, +# you override whatever it chooses. Still, vesa will work most places. +#xserver-xorg xserver-xorg/config/device/driver select vesa + +# A caveat with mouse autodetection is that if it fails, X will retry it +# over and over. So if it's preseeded to be done, there is a possibility of +# an infinite loop if the mouse is not autodetected. +#xserver-xorg xserver-xorg/autodetect_mouse boolean true + +# Monitor autodetection is recommended. +xserver-xorg xserver-xorg/autodetect_monitor boolean true +# Uncomment if you have an LCD display. +#xserver-xorg xserver-xorg/config/monitor/lcd boolean true +# X has three configuration paths for the monitor. Here's how to preseed +# the "medium" path, which is always available. The "simple" path may not +# be available, and the "advanced" path asks too many questions. +xserver-xorg xserver-xorg/config/monitor/selection-method \ + select medium +xserver-xorg xserver-xorg/config/monitor/mode-list \ + select 1024x768 @ 60 Hz + +### Preseeding other packages +# Depending on what software you choose to install, or if things go wrong +# during the installation process, it's possible that other questions may +# be asked. You can preseed those too, of course. To get a list of every +# possible question that could be asked during an install, do an +# installation, and then run these commands: +# debconf-get-selections --installer > file +# debconf-get-selections >> file + + +#### Advanced options +### Running custom commands during the installation +# d-i preseeding is inherently not secure. Nothing in the installer checks +# for attempts at buffer overflows or other exploits of the values of a +# preconfiguration file like this one. Only use preconfiguration files from +# trusted locations! To drive that home, and because it's generally useful, +# here's a way to run any shell command you'd like inside the installer, +# automatically. + +# This first command is run as early as possible, just after +# preseeding is read. +#d-i preseed/early_command string anna-install some-udeb +#d-i preseed/early_command string apt-install curl; wget http://<%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %>/ubuntu_dvd/<%= @install_name %>/net-pre-install.sh -O /tmp/net-pre-install.sh ; chmod +x /tmp/net-pre-install.sh ; //tmp/net-pre-install.sh <%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %> + +# This command is run immediately before the partitioner starts. It may be +# useful to apply dynamic partitioner preseeding that depends on the state +# of the disks (which may not be visible when preseed/early_command runs). +#d-i partman/early_command string debconf-set partman-auto/disk "$(list-devices disk | head -n1)" + +# This command is run just before the install finishes, but when there is +# still a usable /target directory. You can chroot to /target and use it +# directly, or use the apt-install and in-target commands to easily install +# packages and run commands in the target system. +#d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh + +d-i preseed/late_command string wget http://<%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %>/ubuntu_dvd/<%= @install_name %>/net-post-install.sh -O /target/net-post-install.sh ; chmod +x /target/net-post-install.sh ; /target/net-post-install.sh <%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %> + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.answer b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.answer new file mode 100644 index 00000000000..63d53188e46 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.answer @@ -0,0 +1,12 @@ + + + sda + us + mypassword + http://<%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %>/ubuntu_dvd/XenServer_/ + + http://<%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %>/ubuntu_dvd/XenServer/post-install-script + + + Europe/London + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.default b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.default new file mode 100644 index 00000000000..a01ed9b3239 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.default @@ -0,0 +1,8 @@ +DEFAULT install +PROMPT 0 +TIMEOUT 0 + +LABEL install + KERNEL /boot/isolinux/mboot.c32 + APPEND /boot/xen.gz dom0_mem=752M com1=115200,8n1 console=com1,vga --- /boot/vmlinuz xencons=hvc console=hvc0 console=tty0 answerfile=http://<%= @cc_built_admin_node_ip %>:<%= @cc_install_web_port %>/ubuntu_dvd/XenServer/xenserver.answer install --- /install.img + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.post b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.post new file mode 100644 index 00000000000..0d3cadf4a9b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/cookbooks/ubuntu-install/templates/default/xenserver.post @@ -0,0 +1,4 @@ +#!/bin/bash + +wget -q "http://<%= @cc_built_admin_node_ip %>:3000/machines/transition/0.yaml?mac=\$MAC&state=ready" -O- + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/data_bags/crowbar/bc-template-provisioner.json b/crowbar/change-image/dell/barclamps/provisioner/chef/data_bags/crowbar/bc-template-provisioner.json new file mode 100644 index 00000000000..ed8f4d45d26 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/data_bags/crowbar/bc-template-provisioner.json @@ -0,0 +1,49 @@ +{ + "id": "bc-template-provisioner", + "description": "The roles and recipes to set up the provisioning server and a base environment for all nodes", + "attributes": { + "provisioner": { + "default_user": "openstack", + "default_password_hash": "$1$QF9RUAuc$y4ZKSvYo/mPQeZzpuhc0J1", + "web_port": 8091, + "use_local_security": true, + "use_serial_console": false, + "dhcp": { + "lease-time": 60, + "state_machine": { + "web_app_state": "dhcp_state", + "discovered": "hwinstall", + "hardare-installing": "hwinstall", + "hardware-installed": "nova_install", + "hardare-updating": "hwinstall", + "hardware-updated": "execute", + "installed": "execute", + "update": "update", + "ready": "execute", + "reset": "reset", + "delete": "delete", + "reinstall": "nova_install" + } + } + } + }, + "deployment": { + "provisioner": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "provisioner-server" ], + [ "provisioner-base" ] + ], + "config": { + "environment": "provisioner-config-test", + "mode": "full", + "transitions": true, + "transition_list": [ + "all" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/data_bags/crowbar/bc-template-provisioner.schema b/crowbar/change-image/dell/barclamps/provisioner/chef/data_bags/crowbar/bc-template-provisioner.schema new file mode 100644 index 00000000000..aebd2f98605 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/data_bags/crowbar/bc-template-provisioner.schema @@ -0,0 +1,91 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-provisioner-|^bc-template-provisioner$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "provisioner": { + "type": "map", + "required": true, + "mapping": { + "default_user": { "type": "str", "required": true }, + "default_password": { "type": "str" }, + "default_password_hash": { "type": "str" }, + "web_port": { "type": "int", "required": true }, + "use_local_security": { "type": "bool", "required": true }, + "use_serial_console": { "type": "bool", "required": true }, + "dhcp": { + "type": "map", + "required": true, + "mapping": { + "lease-time": { "type": "int", "required": true }, + "state_machine": { + "type": "map", + "required": true, + "mapping": { + =: { + "type": "str", + "required": true + } + } + } + } + } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "provisioner": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/roles/provisioner-base.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/roles/provisioner-base.rb new file mode 100644 index 00000000000..419e18bedab --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/roles/provisioner-base.rb @@ -0,0 +1,13 @@ + +name "provisioner-base" +description "Provisioner Base role - Apt and Networking" +run_list( + "recipe[ubuntu-install::apt_repo]", + "recipe[utils]", + "recipe[barclamp]", + "recipe[apt]", + "recipe[ubuntu-install::base]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/provisioner/chef/roles/provisioner-server.rb b/crowbar/change-image/dell/barclamps/provisioner/chef/roles/provisioner-server.rb new file mode 100644 index 00000000000..2716aee76a2 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/chef/roles/provisioner-server.rb @@ -0,0 +1,13 @@ + +name "provisioner-server" +description "Provisioner Server role - Apt and Networking" +run_list( + "recipe[utils]", + "recipe[dhcp]", + "recipe[tftpd]", + "recipe[nfs-server]", + "recipe[ubuntu-install]" +) +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/provisioner/command_line/crowbar_provisioner b/crowbar/change-image/dell/barclamps/provisioner/command_line/crowbar_provisioner new file mode 100755 index 00000000000..3237c938418 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/command_line/crowbar_provisioner @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "provisioner" + +main diff --git a/crowbar/change-image/dell/barclamps/provisioner/command_line/ipmi_watch b/crowbar/change-image/dell/barclamps/provisioner/command_line/ipmi_watch new file mode 100755 index 00000000000..d7622aafc8e --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/command_line/ipmi_watch @@ -0,0 +1,50 @@ +#!/bin/bash + +print_usage() { + echo " Usage: $0 -h -u -p " +} + +run_watch() { + echo " Connecting to BMC console on $1... " + ipmitool -I lanplus -H "$1" -U "$2" -P "$3" sol activate +} + +run_interactive() { + clear + read -p " IPMI address : " hostname + read -p " Username : " username + prompt=" Password : " + while read -p "${prompt}" -s -n 1 char ; do + if [[ $char == $'\0' ]] ; then + break + fi + prompt='*' + password+="$char" + done + echo +} + +if [ $# -eq 0 ] ; then + run_interactive +else + while getopts 'h:u:p:--help' OPTION ; do + case $OPTION in + h) hostname="$OPTARG" + ;; + u) username="$OPTARG" + ;; + p) password="$OPTARG" + ;; + *) print_usage && exit 1 + ;; + esac + done + if ! [[ $username && $password && $hostname ]] ; then + echo " You have to set a username, password and hostname" + echo " Run ./$0 --help for more information" + exit 1 + fi +fi + +run_watch "$hostname" "$username" "$password" + diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/provisioner/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/compat b/crowbar/change-image/dell/barclamps/provisioner/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/control b/crowbar/change-image/dell/barclamps/provisioner/debian/control new file mode 100644 index 00000000000..3c2d3da2da1 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-provisioner +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-provisioner +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the provisioner barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/copyright b/crowbar/change-image/dell/barclamps/provisioner/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/postinst b/crowbar/change-image/dell/barclamps/provisioner/debian/postinst new file mode 100644 index 00000000000..894f5bb5b69 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-provisioner/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-provisioner/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-provisioner/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/rules b/crowbar/change-image/dell/barclamps/provisioner/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/provisioner/debian/source/format b/crowbar/change-image/dell/barclamps/provisioner/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/provisioner/version.sh b/crowbar/change-image/dell/barclamps/provisioner/version.sh new file mode 100644 index 00000000000..fd71ca41ba3 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/provisioner/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=provisioner +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/barclamps/test/Makefile b/crowbar/change-image/dell/barclamps/test/Makefile new file mode 100644 index 00000000000..33890c5cfe6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/Makefile @@ -0,0 +1,21 @@ + +clean: + @echo "Cleaning barclamp-test" + +distclean: + @echo "Dist-Cleaning barclamp-test" + +all: clean build install + +build: + @echo "Building barclamp-test" + +install: + @echo "Installing barclamp-test" + mkdir -p ${DESTDIR}/opt/crowbar/openstack_manager + cp -r app ${DESTDIR}/opt/crowbar/openstack_manager + mkdir -p ${DESTDIR}/usr/share/barclamp-test + cp -r chef ${DESTDIR}/usr/share/barclamp-test + mkdir -p ${DESTDIR}/usr/bin + cp -r command_line/* ${DESTDIR}/usr/bin + diff --git a/crowbar/change-image/dell/barclamps/test/app/controllers/test_controller.rb b/crowbar/change-image/dell/barclamps/test/app/controllers/test_controller.rb new file mode 100644 index 00000000000..c40d2119752 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/app/controllers/test_controller.rb @@ -0,0 +1,21 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class TestController < BarclampController + def initialize + @service_object = TestService.new logger + end +end + diff --git a/crowbar/change-image/dell/barclamps/test/app/models/test_service.rb b/crowbar/change-image/dell/barclamps/test/app/models/test_service.rb new file mode 100644 index 00000000000..8a7934932fa --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/app/models/test_service.rb @@ -0,0 +1,48 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class TestService < ServiceObject + + def initialize(thelogger) + @bc_name = "test" + @logger = thelogger + end + + def create_proposal + @logger.debug("Test create_proposal: entering") + base = super + @logger.debug("Test create_proposal: leaving base part") + + nodes = NodeObject.find_nodes_by_name "dtest*" + nodes = nodes.sort{|a, b| a.name <=> b.name} + + if nodes.size == 1 + base["deployment"]["test"]["elements"] = { + "test-single" => [ nodes.first.name ] + } + elsif nodes.size > 1 + head = nodes.shift + base["deployment"]["test"]["elements"] = { + "test-multi-head" => [ head.name ], + "test-multi-rest" => nodes.map { |x| x.name } + } + end + + @logger.debug("Test create_proposal: exiting") + base + end + +end + diff --git a/crowbar/change-image/dell/barclamps/test/barclamp-test.spec b/crowbar/change-image/dell/barclamps/test/barclamp-test.spec new file mode 100644 index 00000000000..24abd428af1 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/barclamp-test.spec @@ -0,0 +1,52 @@ + +%define _topdir BUILD_DIR +%define name barclamp-test +%define release RPM_CONTEXT_NUMBER +%define version MAJOR_VERSION.MINOR_VERSION +%define buildroot %{_topdir}/%{name}-%{version}-root + +BuildRoot: %{buildroot} +Summary: provides a shell for writing tests against +License: Apache 2.0 +Name: %{name} +BuildArch: noarch +Version: %{version} +Release: %{release} +Source: %{name}-%{version}.tar.gz +Prefix: / +Group: Development/Tools + +%description +A Crowbar Barclamp that manages test deployments within a Crowbar environment. + +%prep +%setup -q + +%build + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%post +cd /usr/share/barclamp-test/chef/cookbooks +knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + +cd /usr/share/barclamp-test/chef/data_bags/crowbar +for i in *.json; do + knife data bag from file crowbar $i +done + +cd /usr/share/barclamp-test/chef/roles +for i in *.rb; do + knife role from file $i +done + +service httpd graceful + + +%files +%defattr(-,root,root) +/usr/bin +/usr/share +/opt + diff --git a/crowbar/change-image/dell/barclamps/test/build_deb.sh b/crowbar/change-image/dell/barclamps/test/build_deb.sh new file mode 100755 index 00000000000..79bf21d2ba7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/build_deb.sh @@ -0,0 +1,17 @@ +# +# Must have tools: +# apt-get install dpkg-dev debhelper devscripts fakeroot linda dh-make +# + +. ./version.sh + +sed -e "s/CHANGE_LOG_LINE/barclamp-${BARCLAMP_NAME} (${MAJOR_VERSION}.${MINOR_VERSION}-${DEB_CONTEXT_NUMBER}) unstable; urgency=low/" debian/changelog.tmpl > debian/changelog + + +yes | debuild -us -uc + +mkdir -p bin +mv ../barclamp-${BARCLAMP_NAME}_*.deb bin +mv ../barclamp-${BARCLAMP_NAME}_*gz bin +rm ../barclamp-${BARCLAMP_NAME}_* + diff --git a/crowbar/change-image/dell/barclamps/test/build_rpm.sh b/crowbar/change-image/dell/barclamps/test/build_rpm.sh new file mode 100755 index 00000000000..c15d9be527a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/build_rpm.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# +# Needs sudo apt-get install rpm +# + +. ./version.sh + +BUILD_DIR="/tmp/build.$$" +rm -rf ${BUILD_DIR} +mkdir -p ${BUILD_DIR}/BUILD +mkdir -p ${BUILD_DIR}/RPMS +mkdir -p ${BUILD_DIR}/SOURCES +mkdir -p ${BUILD_DIR}/SPECS +mkdir -p ${BUILD_DIR}/SRPMS + +FULL_NAME="barclamp-${BARCLAMP_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}" + +mkdir $FULL_NAME +cp -r Makefile app chef command_line $FULL_NAME +tar -zcf ${BUILD_DIR}/SOURCES/${FULL_NAME}.tar.gz ${FULL_NAME} +rm -rf ${FULL_NAME} + +sed -e "s%BUILD_DIR%$BUILD_DIR%" barclamp-${BARCLAMP_NAME}.spec > ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MAJOR_VERSION%${MAJOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%MINOR_VERSION%${MINOR_VERSION}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec +sed -ie "s%RPM_CONTEXT_NUMBER%${RPM_CONTEXT_NUMBER}%" ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +rpmbuild -v -ba --clean ${BUILD_DIR}/SPECS/barclamp-${BARCLAMP_NAME}.spec + +mkdir -p bin +cp ${BUILD_DIR}/RPMS/noarch/* bin +cp ${BUILD_DIR}/SRPMS/* bin +#rm -rf ${BUILD_DIR} diff --git a/crowbar/change-image/dell/barclamps/test/chef/data_bags/crowbar/bc-template-test.json b/crowbar/change-image/dell/barclamps/test/chef/data_bags/crowbar/bc-template-test.json new file mode 100644 index 00000000000..1ff4988f62d --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/chef/data_bags/crowbar/bc-template-test.json @@ -0,0 +1,28 @@ +{ + "id": "bc-template-test", + "description": "provides a shell for writing tests against", + "attributes": { + "test": { + "param1": "test" + } + }, + "deployment": { + "test": { + "crowbar-revision": 0, + "elements": {}, + "element_order": [ + [ "test-multi-head" ], + [ "test-multi-rest", "test-single" ] + ], + "config": { + "environment": "test-config-test", + "mode": "full", + "transitions": true, + "transition_list": [ + "discovered" + ] + } + } + } +} + diff --git a/crowbar/change-image/dell/barclamps/test/chef/data_bags/crowbar/bc-template-test.schema b/crowbar/change-image/dell/barclamps/test/chef/data_bags/crowbar/bc-template-test.schema new file mode 100644 index 00000000000..b53440cb3d6 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/chef/data_bags/crowbar/bc-template-test.schema @@ -0,0 +1,69 @@ +{ + "type": "map", + "required": true, + "mapping": { + "id": { "type": "str", "required": true, "pattern": "/^bc-test-|^bc-template-test$/" }, + "description": { "type": "str", "required": true }, + "attributes": { + "type": "map", + "required": true, + "mapping": { + "test": { + "type": "map", + "required": true, + "mapping": { + "param1": { "type": "str", "required": true } + } + } + } + }, + "deployment": { + "type": "map", + "required": true, + "mapping": { + "test": { + "type": "map", + "required": true, + "mapping": { + "crowbar-revision": { "type": "int", "required": true }, + "crowbar-committing": { "type": "bool" }, + "crowbar-queued": { "type": "bool" }, + "elements": { + "type": "map", + "required": true, + "mapping": { + = : { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + }, + "element_order": { + "type": "seq", + "required": true, + "sequence": [ { + "type": "seq", + "sequence": [ { "type": "str" } ] + } ] + }, + "config": { + "type": "map", + "required": true, + "mapping": { + "environment": { "type": "str", "required": true }, + "mode": { "type": "str", "required": true }, + "transitions": { "type": "bool", "required": true }, + "transition_list": { + "type": "seq", + "required": true, + "sequence": [ { "type": "str" } ] + } + } + } + } + } + } + } + } +} diff --git a/crowbar/change-image/dell/barclamps/test/chef/roles/test-multi-head.rb b/crowbar/change-image/dell/barclamps/test/chef/roles/test-multi-head.rb new file mode 100644 index 00000000000..c158b225d75 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/chef/roles/test-multi-head.rb @@ -0,0 +1,9 @@ + +name "test-multi-head" +description "Test role for multi-node testing - does nothing" +run_list( +) +default_attributes( +) +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/test/chef/roles/test-multi-rest.rb b/crowbar/change-image/dell/barclamps/test/chef/roles/test-multi-rest.rb new file mode 100644 index 00000000000..b7920849e03 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/chef/roles/test-multi-rest.rb @@ -0,0 +1,7 @@ + +name "multi-test-rest" +description "Test role for multi-node testing - does nothing" +run_list() +default_attributes() +override_attributes() + diff --git a/crowbar/change-image/dell/barclamps/test/chef/roles/test-single.rb b/crowbar/change-image/dell/barclamps/test/chef/roles/test-single.rb new file mode 100644 index 00000000000..0842af979fc --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/chef/roles/test-single.rb @@ -0,0 +1,6 @@ + +name "test-single" +description "Test Role for Single node testing - does nothing" +run_list() +default_attributes() +override_attributes() diff --git a/crowbar/change-image/dell/barclamps/test/command_line/barclamp_test.rb b/crowbar/change-image/dell/barclamps/test/command_line/barclamp_test.rb new file mode 100755 index 00000000000..96823410e88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/command_line/barclamp_test.rb @@ -0,0 +1,200 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "test" +@testcase = "none" + +@allow_zero_args = true +@t_debug = false +@t_exit = false +@rc = 0 + +# Handle extra arguments +def handle_test_debug + @t_debug = true +end + +def handle_test_exit + @t_exit = true +end + + +@options << [ [ '--test_debug', '-t', GetoptLong::NO_ARGUMENT ], "--test_debug or -t to turn on debug of test", "handle_test_debug" ] +@options << [ [ '--test_exit', '-e', GetoptLong::NO_ARGUMENT ], "--test_exit or -e to exit on failure", "handle_test_exit" ] +opt_parse + + +def assertHashAttribute(hash, field, expected, emessage, smessage) + data = hash + fields = field.split(".") + fields.each { |x| data = data.nil? ? nil : data[x] } + assertEqual(data, expected, emessage, smessage) +end + +def assertEqualTimes(valueF, expected, emessage, smessage, times) + count = 0 + while count < times + value = eval(valueF) + if value == expected + puts "#{@testcase}: #{smessage}" if @t_debug + return true + end + sleep 1 + count += 1 + end + puts "#{@testcase}: Value: #{value.inspect} Expected: #{expected.inspect}: #{emessage}" + @rc = @rc + 1 + + exit if @t_exit + false +end + + +def assertEqual(value, expected, emessage, smessage) + if value != expected + puts "#{@testcase}: Value: #{value.inspect} Expected: #{expected.inspect}: #{emessage}" + @rc = @rc + 1 + exit if @t_exit + else + puts "#{@testcase}: #{smessage}" if @t_debug + end + value == expected +end + + +def empty_db_tests + assertEqual(get_json("/"), [[], 200], "Get of list should be an empty list", "Success: empty list returned for /") + assertEqual(get_json("/proposals"), [[], 200], "Get of proposal should be an empty list", + "Success: empty list returned for /proposals") + assertEqual(list()[0], "No current configurations", "CLI: Get of list should be empty", "Success: cli returned empty list for /") + assertEqual(proposal_list()[0], "No current proposals", "CLI: Get of list should be empty", + "Success: cli returned empty list for /proposals") +end + + +# +# This currently only does API testing +# +def api_tests + @testcase = "apt_test" + empty_db_tests + + data = { "id" => "test_p1" }.to_json + assertEqual(put_json("/proposals", data), ["{}", 200], "Failed to API create proposal: test_p1", "Success: create test_p1 proposal") + + assertEqual(put_json("/proposals/fred", data)[1], 405, "Failed to API create with bad URL proposal: test_p1", "Success: create test_p1 proposal with bad url") + + assertEqualTimes("get_json(\"/proposals\")", [["test_p1"], 200], "Failed to API get proposals: test_p1", "Success: list of proposals with test_p1", 60) + + response = get_json("/proposals/test_p1") + assertEqual(response[1], 200, "Failed to show test_p1", "Success: getting test_p1") + + assertEqual(post_json("/proposals/test_p1", response[0].to_json), ["{}", 200], "Failed to edit test_p1", "Success: editting test_p1") + + response[0][:fred] = "bad" + assertEqual(post_json("/proposals/test_p1", response[0].to_json), ["key 'fred:' is undefined.\n", 400], "Failed to fail editting test_p1", "Success: failed to edit test_p1") + + assertEqual(delete_json("/proposals/test_p1"), ["{}", 200], "Failed to API delete proposal: test_p1", "Success: delete test_p1 proposal") + assertEqual(delete_json("/proposals/test_p1"), ["", 404], "Failed to API delete proposal: missing test_p1", "Success: not finding to delete test_p1 proposal") + + data = { "id" => "test_p2" }.to_json + assertEqual(put_json("/proposals", data), ["{}", 200], "Failed to API create proposal: test_p2", "Success: create test_p2 proposal") + assertEqual(post_json("/proposals/commit/test_p2", ""), ["{}", 200], "Failed to commit test_p2", "Success: created committing test_p2") + + assertEqualTimes("get_json(\"/proposals\")", [["test_p2"], 200], "Failed to API get proposals: test_p2", "Success: list of proposals with test_p2", 60) + assertEqualTimes("get_json(\"/\")", [["test_p2"], 200], "Failed to API get: test_p2", "Success: list with test_p2", 60) + + response = get_json("/test_p2") + assertEqual(response[1], 200, "Failed to show test_p2", "Success: getting test_p2") + + assertEqual(delete_json("/test_p2"), ["{}", 200], "Failed to API delete: test_p2", "Success: delete test_p2") + assertEqual(delete_json("/test_p2"), ["", 404], "Failed to API delete: missing test_p2", "Success: delete missing test_p2") + + assertEqual(delete_json("/proposals/test_p2"), ["{}", 200], "Failed to API delete proposal: test_p2", "Success: delete test_p2 proposal") + + empty_db_tests +end + + +def transition_machine (name, state) + oldbc = @barclamp + @barclamp = "crowbar" + data = { "name" => name, "state" => state }.to_json + answer = post_json("/transition/default", data) + assertEqual(answer[1], 200, "Failed to transition #{name}: name test", "Success transition #{name}: result test") + assertEqual(JSON.parse(answer[0])["name"], name, "Failed to transition #{name}: name test", "Success transition #{name}: name test") + + @barclamp = "machines" + assertEqualTimes("get_json(\"/\")[0].include? \"#{name}\"", true, "Failed to find #{name} in node list", "Success: found #{name} in node list", 60) + + @barclamp = oldbc +end + + +# Node Manipulation tests +def node_manipulation + @testcase = "node_manipulate" + @barclamp = "test" + + empty_db_tests + + transition_machine "dtest-machine-1.dell.com", "testing" + + data = { "id" => "test_p1" }.to_json + assertEqual(put_json("/proposals", data), ["{}", 200], "Failed to API create proposal: test_p1", "Success: create test_p1 proposal") + response = get_json("/proposals/test_p1") + assertHashAttribute(response[0], "deployment.test.elements.test-single", [ "dtest-machine-1.dell.com" ], "Failed to add dtest-machine-1.dell.com to proposal", "Success creating proposal with test machine single") + assertHashAttribute(response[0], "deployment.test.elements.test-multi-head", nil, "Failed with values in test-multi-head to proposal", "Success creating proposal without test-multi-head") + assertHashAttribute(response[0], "deployment.test.elements.test-multi-rest", nil, "Failed with values in test-multi-head to proposal", "Success creating proposal without test-multi-rest") + + transition_machine "dtest-machine-2.dell.com", "testing" + + assertEqual(post_json("/proposals/commit/test_p1", ""), ["[\"dtest-machine-1.dell.com\"]", 202], "Failed to queue commit test_p1", "Success: queued committing test_p1") + + response = get_json("/test_p1") + assertEqual(response[1], 404, "Found test_p1", "Success: not finding active test_p1") + + transition_machine "dtest-machine-1.dell.com", "ready" + + response = get_json("/test_p1") + assertEqual(response[1], 200, "Didn't find test_p1", "Success: found active test_p1") + + response = get_json("/test_p1") + assertHashAttribute(response[0], "deployment.test.elements.test-single", [ "dtest-machine-1.dell.com" ], "Failed to add dtest-machine-1.dell.com to role", "Success creating role with test machine single") + assertHashAttribute(response[0], "deployment.test.elements.test-multi-head", nil, "Failed with values in test-multi-head to role", "Success creating role without test-multi-head") + assertHashAttribute(response[0], "deployment.test.elements.test-multi-rest", nil, "Failed with values in test-multi-head to role", "Success creating role without test-multi-rest") + + # GREG: test machines have roles. + + assertEqual(delete_json("/proposals/test_p1"), ["{}", 200], "Failed to delete proposal: test_p1", "Success: delete test_p1 proposal") + assertEqual(delete_json("/test_p1"), ["{}", 200], "Failed to delete active: test_p1", "Success: delete test_p1 active") + + # GREG: Check that machines don't have roles. + + empty_db_tests + + # Test multiple nodes + data = { "id" => "test_p1" }.to_json + assertEqual(put_json("/proposals", data), ["{}", 200], "Failed to API create proposal: test_p1", "Success: create test_p1 proposal") + response = get_json("/proposals/test_p1") + assertHashAttribute(response[0], "deployment.test.elements.test-single", nil, "Failed with values in test-single to role", "Success creating role without test machine single") + assertHashAttribute(response[0], "deployment.test.elements.test-multi-head", [ "dtest-machine-1.dell.com" ], "Failed to add dtest-machine-1.dell.com to proposal", "Success creating proposal with test-multi-head") + assertHashAttribute(response[0], "deployment.test.elements.test-multi-rest", [ "dtest-machine-2.dell.com" ], "Failed to add dtest-machine-2.dell.com to proposal", "Success creating proposal with test-multi-rest") + + assertEqual(delete_json("/proposals/test_p1"), ["{}", 200], "Failed to delete proposal: test_p1", "Success: delete test_p1 proposal") + + empty_db_tests + + system("knife node -y delete dtest-machine-1.dell.com") + system("knife node -y delete dtest-machine-2.dell.com") +end + + +# API Tests +api_tests + +# Node Manipulation tests +node_manipulation + +exit @rc + diff --git a/crowbar/change-image/dell/barclamps/test/command_line/crowbar_test b/crowbar/change-image/dell/barclamps/test/command_line/crowbar_test new file mode 100755 index 00000000000..bf07a3ef48b --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/command_line/crowbar_test @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require File.join(File.expand_path(File.dirname(__FILE__)), "barclamp_lib") +@barclamp = "test" + +main diff --git a/crowbar/change-image/dell/barclamps/test/debian/changelog.tmpl b/crowbar/change-image/dell/barclamps/test/debian/changelog.tmpl new file mode 100644 index 00000000000..42694be8134 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/changelog.tmpl @@ -0,0 +1,5 @@ +CHANGE_LOG_LINE + + * Initial Release. + + -- unknown Thu, 19 May 2011 15:50:02 -0500 diff --git a/crowbar/change-image/dell/barclamps/test/debian/compat b/crowbar/change-image/dell/barclamps/test/debian/compat new file mode 100644 index 00000000000..7f8f011eb73 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/compat @@ -0,0 +1 @@ +7 diff --git a/crowbar/change-image/dell/barclamps/test/debian/control b/crowbar/change-image/dell/barclamps/test/debian/control new file mode 100644 index 00000000000..1dc09dc90c7 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/control @@ -0,0 +1,13 @@ +Source: barclamp-test +Section: unknown +Priority: extra +Maintainer: Dell Openstack +Build-Depends: debhelper (>= 7.0.50~) +Standards-Version: 3.9.1 +Homepage: http://openstack.dell.com/ + +Package: barclamp-test +Architecture: all +Depends: barclamp-crowbar +Description: Ganglia Barclamp for Crowbar + Provides the test barclamp for crowbar diff --git a/crowbar/change-image/dell/barclamps/test/debian/copyright b/crowbar/change-image/dell/barclamps/test/debian/copyright new file mode 100644 index 00000000000..ef293d897c8 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/copyright @@ -0,0 +1,26 @@ +This work was packaged for Debian by: + + Dell Openstack Team on Thu, 19 May 2011 15:50:02 -0500 + +It was downloaded from: + + + +Upstream Author(s): + + Openstack Dell Team + +Copyright: + + Copyright (C) 2011 Dell, Inc. + +License: + +GREG: Fill in Apache 2.0 + +The Debian packaging is: + + Copyright (C) 2011 Dell + +and is licensed under Apache 2.0, see above. + diff --git a/crowbar/change-image/dell/barclamps/test/debian/postinst b/crowbar/change-image/dell/barclamps/test/debian/postinst new file mode 100644 index 00000000000..da6ed3fff17 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/postinst @@ -0,0 +1,53 @@ +#!/bin/sh +# postinst script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + cd /usr/share/barclamp-test/chef/cookbooks + knife cookbook upload -o . -a -u chef-webui -k /etc/chef/webui.pem + + cd /usr/share/barclamp-test/chef/data_bags/crowbar + for i in *.json; do + knife data bag from file crowbar $i + done + + cd /usr/share/barclamp-test/chef/roles + for i in *.rb; do + knife role from file $i + done + + service apache2 graceful + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/crowbar/change-image/dell/barclamps/test/debian/rules b/crowbar/change-image/dell/barclamps/test/debian/rules new file mode 100755 index 00000000000..79fd842dcae --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/crowbar/change-image/dell/barclamps/test/debian/source/format b/crowbar/change-image/dell/barclamps/test/debian/source/format new file mode 100644 index 00000000000..89ae9db8f88 --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/crowbar/change-image/dell/barclamps/test/version.sh b/crowbar/change-image/dell/barclamps/test/version.sh new file mode 100644 index 00000000000..3cdd637e30a --- /dev/null +++ b/crowbar/change-image/dell/barclamps/test/version.sh @@ -0,0 +1,7 @@ +BARCLAMP_NAME=test +MAJOR_VERSION=0 +MINOR_VERSION=8 +SVN_REVISION=${SVN_REVISION:-custom} +BUILD_NUMBER=${BUILD_NUMBER:-custom} +RPM_CONTEXT_NUMBER=${SVN_REVISION}_${BUILD_NUMBER} +DEB_CONTEXT_NUMBER=${SVN_REVISION}-${BUILD_NUMBER} diff --git a/crowbar/change-image/dell/blocking_chef_client.sh b/crowbar/change-image/dell/blocking_chef_client.sh new file mode 100755 index 00000000000..c1b70a3ff3c --- /dev/null +++ b/crowbar/change-image/dell/blocking_chef_client.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +lockfile="/tmp/chef-client.lock" +while ! ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; do + pid=$(cat "$lockfile") + echo "Failed to aquire lockfile: $lockfile." + echo "Held by $(readlink "/proc/$pid/exe") ($pid)" + echo "Process tree for $pid:" + pstree -Aupcl "$pid" + echo + sleep 1 +done +trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT +echo "$$" >"$lockfile" +ret=0 +for loglvl in info debug; do + chef-client -l "$loglvl" -V && break + case $loglvl in + info) echo "Chef client run failed, will retry with debugging.";; + debug) echo "Chef client run failed with debug enabled."; ret=1;; + esac +done +rm -f "$lockfile" +trap - INT TERM EXIT +exit $ret diff --git a/crowbar/change-image/dell/chef/cookbooks/README b/crowbar/change-image/dell/chef/cookbooks/README new file mode 100644 index 00000000000..e7f47f24cc7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/README @@ -0,0 +1,34 @@ +Download cookbooks into this directory from the Opscode Cookbooks site +using knife, or remove this file to clone an upstream Git Repository. + +DEFINITELY USED +apache - no changes +apt - no changes +mysql - ubuntu-10.10 templates +nscd - no changes +openldap - potentially modified +openssl - no changes +rabbitmq - no changes +openssh - no changes +glance -duh +nova - duh +swift - duh +build-essential - added ruby-dev, move patch up +runit - no changes + + +POTENTIALLY UNUSED +anso - Anso specific scripts +bluepill - no changes +chef - no changes +couchdb - no changes +daemontools - no changes +erlang - no changes +java - no changes +nginx - looks like no changs, old version here +ohai - no changes +rabbitmq_chef - no changes +ucspi-tcp - no changes +xml - no changes +zlib - no changes + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/README.rdoc b/crowbar/change-image/dell/chef/cookbooks/apache2/README.rdoc new file mode 100644 index 00000000000..e5e2c5440b6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/README.rdoc @@ -0,0 +1,78 @@ += DESCRIPTION: + +Complete Debian/Ubuntu style Apache2 configuration. + += REQUIREMENTS: + +Debian or Ubuntu preferred. + +Red Hat, CentOS, Fedora and ArchLinux can be used but will be converted to a Debian/Ubuntu style Apache as it's far easier to manage with Chef. + += ATTRIBUTES: + +The file attributes/apache.rb contains the following attribute types: + +* platform specific locations and settings. +* general settings +* prefork attributes +* worker attributes + +General settings and prefork/worker attributes are tunable. + += USAGE: + +Include the apache2 recipe to install Apache2 and get 'sane' default settings. Configuration is modularized through Apache vhost sites a la Debian style configuration. + +For Red Hat, CentOS and Fedora you should first disable selinux as it's not supported (yet), then remove the stock httpd and all it's dependencies prior to attempting to use this recipe. Many packages in these distributions drop conflicting configs into conf.d, all of which haven't been accounted for yet. Starting from scratch will also make it far easier to debug. + +== Defines: + +* +apache_module+: sets up an Apache module. +* +apache_conf+: sets up a config file for an apache module. +* +apache_site+: sets up a vhost site. The conf file must be available. +* +web_app+: copies the template for a web app and enables it as a site via +apache_site+. + +== Web Apps: + +Various applications that can be set up with Apache as the front end, such as PHP, Django, Rails and others can use the web_app define to set up the template and the Apache site. The define is kind of dumb, so the template needs have the application implementation settings, since we don't know what your app is or what is needed from Apache. + +We only prototype one parameter for the +web_app+ define, "template". This is used to specify the name of the template to use in the current cookbook. When you use +web_app+, you can set up any parameters you want to use in your template. They will get passed to the template through the params hash. For example, the sample +web_app.conf.erb+ template in this cookbook makes use of these. + +* +docroot+ +* +server_name+ +* +server_aliases+ + +These are available as +params[:docroot]+, +params[:server_name]+, +params[:server_aliases]+ prefixed with an @ within the template. + +If 'cookbook' and 'template' are not specified, the current cookbook's +templates/default/web_app.conf.erb+ will be used. If this template is not suitable for your application, copy it to your cookbook and customize as needed. + +== God Monitor: + +There's a new recipe, +apache2::god_monitor+. You will need to make sure to include the 'god' recipe before using the +apache2::god_monitor+ recipe in your cookbook. + +== OpenID Auth + +Installs the +mod_auth_openid+ module from source. Specify an array of OpenIDs that are allowed to authenticate with the attribute +apache[:allowed_openids]+. Use the following in a vhost to protect with OpenID authentication: + + AuthOpenIDEnabled On + AuthOpenIDDBLocation /var/cache/apache2/mod_auth_openid.db + AuthOpenIDUserProgram /usr/local/bin/mod_auth_openid.rb + +Change the DBLocation as appropriate for your platform. You'll need to change the file in the recipe to match. The UserProgram is optional if you don't want to limit access by certain OpenIDs. + += LICENSE & AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/attributes/default.rb new file mode 100644 index 00000000000..ba6c48cb29a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/attributes/default.rb @@ -0,0 +1,87 @@ +# +# Cookbook Name:: apache2 +# Attributes:: apache +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Where the various parts of apache are +case platform +when "redhat","centos","fedora","suse" + set[:apache][:dir] = "/etc/httpd" + set[:apache][:log_dir] = "/var/log/httpd" + set[:apache][:user] = "apache" + set[:apache][:binary] = "/usr/sbin/httpd" + set[:apache][:icondir] = "/var/www/icons/" + set[:apache][:cache_dir] = "/var/cache/httpd" +when "debian","ubuntu" + set[:apache][:dir] = "/etc/apache2" + set[:apache][:log_dir] = "/var/log/apache2" + set[:apache][:user] = "www-data" + set[:apache][:binary] = "/usr/sbin/apache2" + set[:apache][:icondir] = "/usr/share/apache2/icons" + set[:apache][:cache_dir] = "/var/cache/apache2" +when "arch" + set[:apache][:dir] = "/etc/httpd" + set[:apache][:log_dir] = "/var/log/httpd" + set[:apache][:user] = "http" + set[:apache][:binary] = "/usr/sbin/httpd" + set[:apache][:icondir] = "/usr/share/httpd/icons" + set[:apache][:cache_dir] = "/var/cache/httpd" +else + set[:apache][:dir] = "/etc/apache2" + set[:apache][:log_dir] = "/var/log/apache2" + set[:apache][:user] = "www-data" + set[:apache][:binary] = "/usr/sbin/apache2" + set[:apache][:icondir] = "/usr/share/apache2/icons" + set[:apache][:cache_dir] = "/var/cache/apache2" +end + +### +# These settings need the unless, since we want them to be tunable, +# and we don't want to override the tunings. +### + +# General settings +default[:apache][:listen_ports] = [ "80","443" ] +default[:apache][:contact] = "ops@example.com" +default[:apache][:timeout] = 300 +default[:apache][:keepalive] = "On" +default[:apache][:keepaliverequests] = 100 +default[:apache][:keepalivetimeout] = 5 + +# Security +default[:apache][:servertokens] = "Prod" +default[:apache][:serversignature] = "On" +default[:apache][:traceenable] = "On" + +# mod_auth_openids +default[:apache][:allowed_openids] = Array.new + +# Prefork Attributes +default[:apache][:prefork][:startservers] = 16 +default[:apache][:prefork][:minspareservers] = 16 +default[:apache][:prefork][:maxspareservers] = 32 +default[:apache][:prefork][:serverlimit] = 400 +default[:apache][:prefork][:maxclients] = 400 +default[:apache][:prefork][:maxrequestsperchild] = 10000 + +# Worker Attributes +default[:apache][:worker][:startservers] = 4 +default[:apache][:worker][:maxclients] = 1024 +default[:apache][:worker][:minsparethreads] = 64 +default[:apache][:worker][:maxsparethreads] = 192 +default[:apache][:worker][:threadsperchild] = 64 +default[:apache][:worker][:maxrequestsperchild] = 0 diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_conf.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_conf.rb new file mode 100644 index 00000000000..92a0eca2dcc --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_conf.rb @@ -0,0 +1,26 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_conf +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_conf do + template "#{node[:apache][:dir]}/mods-available/#{params[:name]}.conf" do + source "mods/#{params[:name]}.conf.erb" + notifies :reload, resources(:service => "apache2") + mode 0644 + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_module.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_module.rb new file mode 100644 index 00000000000..fedc0eaf355 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_module.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_module +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_module, :enable => true, :conf => false do + include_recipe "apache2" + + if params[:conf] + apache_conf params[:name] + end + + if params[:enable] + execute "a2enmod #{params[:name]}" do + command "/usr/sbin/a2enmod #{params[:name]}" + notifies :reload, resources(:service => "apache2") + not_if do (File.symlink?("#{node[:apache][:dir]}/mods-enabled/#{params[:name]}.load") and + ((File.exists?("#{node[:apache][:dir]}/mods-available/#{params[:name]}.conf"))? + (File.symlink?("#{node[:apache][:dir]}/mods-enabled/#{params[:name]}.conf")):(true))) + end + end + else + execute "a2dismod #{params[:name]}" do + command "/usr/sbin/a2dismod #{params[:name]}" + notifies :reload, resources(:service => "apache2") + only_if do ::File.symlink?("#{node[:apache][:dir]}/mods-enabled/#{params[:name]}.load") end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_site.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_site.rb new file mode 100644 index 00000000000..1f75e55a30d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/apache_site.rb @@ -0,0 +1,40 @@ +# +# Cookbook Name:: apache2 +# Definition:: apache_site +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :apache_site, :enable => true do + include_recipe "apache2" + + if params[:enable] + execute "a2ensite #{params[:name]}" do + command "/usr/sbin/a2ensite #{params[:name]}" + notifies :reload, resources(:service => "apache2") + not_if do + ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") or + ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/000-#{params[:name]}") + end + only_if do ::File.exists?("#{node[:apache][:dir]}/sites-available/#{params[:name]}") end + end + else + execute "a2dissite #{params[:name]}" do + command "/usr/sbin/a2dissite #{params[:name]}" + notifies :reload, resources(:service => "apache2") + only_if do ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/web_app.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/web_app.rb new file mode 100644 index 00000000000..48342d04886 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/definitions/web_app.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: apache2 +# Definition:: web_app +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :web_app, :template => "web_app.conf.erb" do + + application_name = params[:name] + + include_recipe "apache2" + include_recipe "apache2::mod_rewrite" + include_recipe "apache2::mod_deflate" + include_recipe "apache2::mod_headers" + + template "#{node[:apache][:dir]}/sites-available/#{application_name}.conf" do + source params[:template] + owner "root" + group "root" + mode 0644 + if params[:cookbook] + cookbook params[:cookbook] + end + variables( + :application_name => application_name, + :params => params + ) + if ::File.exists?("#{node[:apache][:dir]}/sites-enabled/#{application_name}.conf") + notifies :reload, resources(:service => "apache2"), :delayed + end + end + + apache_site "#{params[:name]}.conf" do + enable enable_setting + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl b/crowbar/change-image/dell/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl new file mode 100644 index 00000000000..83f849ed961 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/files/default/apache2_module_conf_generate.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl + +=begin + +Generates Ubuntu style module.load files. + +./apache2_module_conf_generate.pl /usr/lib64/httpd/modules /etc/httpd/mods-available + +ARGV[0] is the apache modules directory, ARGV[1] is where you want 'em. + +=cut + +use File::Find; + +use strict; +use warnings; + +die "Must have '/path/to/modules' and '/path/to/modules.load'" + unless $ARGV[0] && $ARGV[1]; + +find( + { + wanted => sub { + return 1 if $File::Find::name !~ /\.so$/; + my $modfile = $_; + $modfile =~ /(lib|mod_)(.+)\.so$/; + my $modname = $2; + my $filename = "$ARGV[1]/$modname.load"; + unless ( -f $filename ) { + open( FILE, ">", $filename ) or die "Cannot open $filename"; + print FILE "LoadModule " . $modname . "_module $File::Find::name\n"; + close(FILE); + } + }, + follow => 1, + }, + $ARGV[0] +); + +exit 0; + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/metadata.json b/crowbar/change-image/dell/chef/cookbooks/apache2/metadata.json new file mode 100644 index 00000000000..8dcb1de2d82 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/metadata.json @@ -0,0 +1,507 @@ +{ + "platforms": { + "debian": [ + + ], + "centos": [ + + ], + "arch": [ + + ], + "ubuntu": [ + + ], + "redhat": [ + + ] + }, + "suggestions": { + + }, + "license": "Apache 2.0", + "conflicting": { + + }, + "long_description": "= DESCRIPTION:\n\nComplete Debian/Ubuntu style Apache2 configuration.\n\n= REQUIREMENTS:\n\nDebian or Ubuntu preferred.\n\nRed Hat, CentOS, Fedora and ArchLinux can be used but will be converted to a Debian/Ubuntu style Apache as it's far easier to manage with Chef.\n\n= ATTRIBUTES:\n\nThe file attributes/apache.rb contains the following attribute types:\n\n* platform specific locations and settings.\n* general settings\n* prefork attributes\n* worker attributes\n\nGeneral settings and prefork/worker attributes are tunable.\n\n= USAGE:\n\nInclude the apache2 recipe to install Apache2 and get 'sane' default settings. Configuration is modularized through Apache vhost sites a la Debian style configuration.\n\nFor Red Hat, CentOS and Fedora you should first disable selinux as it's not supported (yet), then remove the stock httpd and all it's dependencies prior to attempting to use this recipe. Many packages in these distributions drop conflicting configs into conf.d, all of which haven't been accounted for yet. Starting from scratch will also make it far easier to debug.\n\n== Defines:\n\n* +apache_module+: sets up an Apache module.\n* +apache_conf+: sets up a config file for an apache module.\n* +apache_site+: sets up a vhost site. The conf file must be available.\n* +web_app+: copies the template for a web app and enables it as a site via +apache_site+.\n\n== Web Apps:\n\nVarious applications that can be set up with Apache as the front end, such as PHP, Django, Rails and others can use the web_app define to set up the template and the Apache site. The define is kind of dumb, so the template needs have the application implementation settings, since we don't know what your app is or what is needed from Apache.\n\nWe only prototype one parameter for the +web_app+ define, \"template\". This is used to specify the name of the template to use in the current cookbook. When you use +web_app+, you can set up any parameters you want to use in your template. They will get passed to the template through the params hash. For example, the sample +web_app.conf.erb+ template in this cookbook makes use of these.\n\n* +docroot+\n* +server_name+\n* +server_aliases+\n\nThese are available as +params[:docroot]+, +params[:server_name]+, +params[:server_aliases]+ prefixed with an @ within the template.\n\nIf 'cookbook' and 'template' are not specified, the current cookbook's +templates/default/web_app.conf.erb+ will be used. If this template is not suitable for your application, copy it to your cookbook and customize as needed.\n\n== God Monitor:\n\nThere's a new recipe, +apache2::god_monitor+. You will need to make sure to include the 'god' recipe before using the +apache2::god_monitor+ recipe in your cookbook.\n\n== OpenID Auth\n\nInstalls the +mod_auth_openid+ module from source. Specify an array of OpenIDs that are allowed to authenticate with the attribute +apache[:allowed_openids]+. Use the following in a vhost to protect with OpenID authentication:\n\n AuthOpenIDEnabled On\n AuthOpenIDDBLocation /var/cache/apache2/mod_auth_openid.db\n AuthOpenIDUserProgram /usr/local/bin/mod_auth_openid.rb\n\nChange the DBLocation as appropriate for your platform. You'll need to change the file in the recipe to match. The UserProgram is optional if you don't want to limit access by certain OpenIDs.\n\n= LICENSE & AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "providing": { + + }, + "description": "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions", + "version": "0.99.3", + "maintainer": "Opscode, Inc.", + "replacing": { + + }, + "attributes": { + "apache/traceenable": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "On", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Trace Enable", + "description": "Determine behavior of TRACE requests" + }, + "apache/serversignature": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "On", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Server Signature", + "description": "Configure footer on server-generated documents" + }, + "apache/contact": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "ops@example.com", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Contact", + "description": "Email address of webmaster" + }, + "apache/icondir": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "/usr/share/apache2/icons", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Icondir", + "description": "Directory location for icons" + }, + "apache/user": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "www-data", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache User", + "description": "User Apache runs as" + }, + "apache/worker/threadsperchild": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "64", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Worker MPM ThreadsPerChild", + "description": "Constant number of worker threads in each server process" + }, + "apache/worker/maxclients": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "1024", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Worker MPM MaxClients", + "description": "Maximum number of simultaneous connections" + }, + "apache/worker": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "type": "hash", + "recipes": [ + + ], + "display_name": "Apache Worker", + "description": "Hash of Apache prefork tuning attributes." + }, + "apache/keepaliverequests": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "100", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Keepalive Requests", + "description": "Number of requests allowed on a persistent connection" + }, + "apache/timeout": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "300", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Timeout", + "description": "Connection timeout value" + }, + "apache/worker/maxrequestsperchild": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "0", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Worker MPM MaxRequestsPerChild", + "description": "Maximum number of request a child process will handle" + }, + "apache/dir": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "/etc/apache2", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Directory", + "description": "Location for Apache configuration" + }, + "apache/prefork/serverlimit": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "400", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Prefork MPM ServerLimit", + "description": "Upper limit on configurable server processes" + }, + "apache/prefork/minspareservers": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "16", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Prefork MPM MinSpareServers", + "description": "Minimum number of spare server processes" + }, + "apache/allowed_openids": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Allowed OpenIDs", + "description": "Array of OpenIDs allowed to authenticate" + }, + "apache/binary": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "/usr/sbin/apache2", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Binary", + "description": "Apache server daemon program" + }, + "apache/prefork/startservers": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "16", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Prefork MPM StartServers", + "description": "Number of MPM servers to start" + }, + "apache/listen_ports": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": [ + "80", + "443" + ], + "type": "array", + "recipes": [ + + ], + "display_name": "Apache Listen Ports", + "description": "Ports that Apache should listen on" + }, + "apache/prefork/maxrequestsperchild": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "10000", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Prefork MPM MaxRequestsPerChild", + "description": "Maximum number of request a child process will handle" + }, + "apache/keepalivetimeout": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "5", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Keepalive Timeout", + "description": "Time to wait for requests on persistent connection" + }, + "apache/keepalive": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "On", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Keepalive", + "description": "HTTP persistent connections" + }, + "apache": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "type": "hash", + "recipes": [ + + ], + "display_name": "Apache Hash", + "description": "Hash of Apache attributes" + }, + "apache/worker/maxsparethreads": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "192", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Worker MPM MaxSpareThreads", + "description": "Maximum number of spare worker threads" + }, + "apache/worker/startservers": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "4", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Worker MPM StartServers", + "description": "Initial number of server processes to start" + }, + "apache/prefork/maxclients": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "400", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Prefork MPM MaxClients", + "description": "Maximum number of simultaneous connections" + }, + "apache/prefork": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "type": "hash", + "recipes": [ + + ], + "display_name": "Apache Prefork", + "description": "Hash of Apache prefork tuning attributes." + }, + "apache/servertokens": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "Prod", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Server Tokens", + "description": "Server response header" + }, + "apache/log_dir": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "/etc/apache2", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Log Directory", + "description": "Location for Apache logs" + }, + "apache/worker/minsparethreads": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "64", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Worker MPM MinSpareThreads", + "description": "Minimum number of spare worker threads" + }, + "apache/prefork/maxspareservers": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "32", + "type": "string", + "recipes": [ + + ], + "display_name": "Apache Prefork MPM MaxSpareServers", + "description": "Maximum number of spare server processes" + } + }, + "maintainer_email": "cookbooks@opscode.com", + "name": "apache2", + "recipes": { + "apache2::mod_dav": "Apache module 'dav'", + "apache2::mod_proxy_ajp": "Apache module 'proxy_ajp'", + "apache2::mod_php5": "Apache module 'php5'", + "apache2::mod_mime": "Apache module 'mime' with config file", + "apache2::mod_deflate": "Apache module 'deflate' with config file", + "apache2::mod_ssl": "Apache module 'ssl' with config file, adds port 443 to listen_ports", + "apache2::mod_setenvif": "Apache module 'setenvif' with config file", + "apache2::mod_python": "Apache module 'python'", + "apache2::mod_negotiation": "Apache module 'negotiation' with config file", + "apache2::mod_cgi": "Apache module 'cgi'", + "apache2::mod_authz_groupfile": "Apache module 'authz_groupfile'", + "apache2::mod_auth_basic": "Apache module 'auth_basic'", + "apache2::mod_rewrite": "Apache module 'rewrite'", + "apache2::mod_dav_svn": "Apache module 'dav_svn'", + "apache2::mod_autoindex": "Apache module 'autoindex' with config file", + "apache2::mod_authz_host": "Apache module 'authz_host'", + "apache2::mod_headers": "Apache module 'headers'", + "apache2::mod_authz_user": "Apache module 'authz_user'", + "apache2::mod_proxy_connect": "Apache module 'proxy_connect'", + "apache2::mod_proxy": "Apache module 'proxy' with config file", + "apache2::mod_fcgid": "Apache module 'fcgid', package on ubuntu/debian, rhel/centos, compile source on suse; with config file", + "apache2::mod_authnz_ldap": "Apache module 'authnz_ldap'", + "apache2::mod_auth_digest": "Apache module 'auth_digest'", + "apache2::mod_env": "Apache module 'env'", + "apache2::mod_status": "Apache module 'status' with config file", + "apache2::mod_proxy_http": "Apache module 'proxy_http'", + "apache2::mod_log_config": "Apache module 'log_config'", + "apache2::mod_ldap": "Apache module 'ldap'", + "apache2::mod_expires": "Apache module 'expires'", + "apache2::mod_alias": "Apache module 'alias' with config file", + "apache2": "Main Apache configuration", + "apache2::mod_authn_file": "Apache module 'authn_file'", + "apache2::mod_auth_openid": "Apache module 'authopenid'", + "apache2::mod_proxy_balancer": "Apache module 'proxy_balancer'", + "apache2::mod_dir": "Apache module 'dir' with config file", + "apache2::mod_authz_default": "Apache module 'authz_default'" + }, + "groupings": { + + }, + "dependencies": { + + }, + "recommendations": { + + } +} \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/metadata.rb new file mode 100644 index 00000000000..40d0b948b4e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/metadata.rb @@ -0,0 +1,197 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures all aspects of apache2 using Debian style symlinks with helper definitions" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.99.3" +recipe "apache2", "Main Apache configuration" +recipe "apache2::mod_alias", "Apache module 'alias' with config file" +recipe "apache2::mod_auth_basic", "Apache module 'auth_basic'" +recipe "apache2::mod_auth_digest", "Apache module 'auth_digest'" +recipe "apache2::mod_auth_openid", "Apache module 'authopenid'" +recipe "apache2::mod_authn_file", "Apache module 'authn_file'" +recipe "apache2::mod_authnz_ldap", "Apache module 'authnz_ldap'" +recipe "apache2::mod_authz_default", "Apache module 'authz_default'" +recipe "apache2::mod_authz_groupfile", "Apache module 'authz_groupfile'" +recipe "apache2::mod_authz_host", "Apache module 'authz_host'" +recipe "apache2::mod_authz_user", "Apache module 'authz_user'" +recipe "apache2::mod_autoindex", "Apache module 'autoindex' with config file" +recipe "apache2::mod_cgi", "Apache module 'cgi'" +recipe "apache2::mod_dav", "Apache module 'dav'" +recipe "apache2::mod_dav_svn", "Apache module 'dav_svn'" +recipe "apache2::mod_deflate", "Apache module 'deflate' with config file" +recipe "apache2::mod_dir", "Apache module 'dir' with config file" +recipe "apache2::mod_env", "Apache module 'env'" +recipe "apache2::mod_expires", "Apache module 'expires'" +recipe "apache2::mod_fcgid", "Apache module 'fcgid', package on ubuntu/debian, rhel/centos, compile source on suse; with config file" +recipe "apache2::mod_headers", "Apache module 'headers'" +recipe "apache2::mod_ldap", "Apache module 'ldap'" +recipe "apache2::mod_log_config", "Apache module 'log_config'" +recipe "apache2::mod_mime", "Apache module 'mime' with config file" +recipe "apache2::mod_negotiation", "Apache module 'negotiation' with config file" +recipe "apache2::mod_php5", "Apache module 'php5'" +recipe "apache2::mod_proxy", "Apache module 'proxy' with config file" +recipe "apache2::mod_proxy_ajp", "Apache module 'proxy_ajp'" +recipe "apache2::mod_proxy_balancer", "Apache module 'proxy_balancer'" +recipe "apache2::mod_proxy_connect", "Apache module 'proxy_connect'" +recipe "apache2::mod_proxy_http", "Apache module 'proxy_http'" +recipe "apache2::mod_python", "Apache module 'python'" +recipe "apache2::mod_rewrite", "Apache module 'rewrite'" +recipe "apache2::mod_setenvif", "Apache module 'setenvif' with config file" +recipe "apache2::mod_ssl", "Apache module 'ssl' with config file, adds port 443 to listen_ports" +recipe "apache2::mod_status", "Apache module 'status' with config file" + +%w{redhat centos debian ubuntu arch}.each do |os| + supports os +end + +attribute "apache", + :display_name => "Apache Hash", + :description => "Hash of Apache attributes", + :type => "hash" + +attribute "apache/dir", + :display_name => "Apache Directory", + :description => "Location for Apache configuration", + :default => "/etc/apache2" + +attribute "apache/log_dir", + :display_name => "Apache Log Directory", + :description => "Location for Apache logs", + :default => "/etc/apache2" + +attribute "apache/user", + :display_name => "Apache User", + :description => "User Apache runs as", + :default => "www-data" + +attribute "apache/binary", + :display_name => "Apache Binary", + :description => "Apache server daemon program", + :default => "/usr/sbin/apache2" + +attribute "apache/icondir", + :display_name => "Apache Icondir", + :description => "Directory location for icons", + :default => "/usr/share/apache2/icons" + +attribute "apache/listen_ports", + :display_name => "Apache Listen Ports", + :description => "Ports that Apache should listen on", + :type => "array", + :default => [ "80", "443" ] + +attribute "apache/contact", + :display_name => "Apache Contact", + :description => "Email address of webmaster", + :default => "ops@example.com" + +attribute "apache/timeout", + :display_name => "Apache Timeout", + :description => "Connection timeout value", + :default => "300" + +attribute "apache/keepalive", + :display_name => "Apache Keepalive", + :description => "HTTP persistent connections", + :default => "On" + +attribute "apache/keepaliverequests", + :display_name => "Apache Keepalive Requests", + :description => "Number of requests allowed on a persistent connection", + :default => "100" + +attribute "apache/keepalivetimeout", + :display_name => "Apache Keepalive Timeout", + :description => "Time to wait for requests on persistent connection", + :default => "5" + +attribute "apache/servertokens", + :display_name => "Apache Server Tokens", + :description => "Server response header", + :default => "Prod" + +attribute "apache/serversignature", + :display_name => "Apache Server Signature", + :description => "Configure footer on server-generated documents", + :default => "On" + +attribute "apache/traceenable", + :display_name => "Apache Trace Enable", + :description => "Determine behavior of TRACE requests", + :default => "On" + +attribute "apache/allowed_openids", + :display_name => "Apache Allowed OpenIDs", + :description => "Array of OpenIDs allowed to authenticate", + :default => "" + +attribute "apache/prefork", + :display_name => "Apache Prefork", + :description => "Hash of Apache prefork tuning attributes.", + :type => "hash" + +attribute "apache/prefork/startservers", + :display_name => "Apache Prefork MPM StartServers", + :description => "Number of MPM servers to start", + :default => "16" + +attribute "apache/prefork/minspareservers", + :display_name => "Apache Prefork MPM MinSpareServers", + :description => "Minimum number of spare server processes", + :default => "16" + +attribute "apache/prefork/maxspareservers", + :display_name => "Apache Prefork MPM MaxSpareServers", + :description => "Maximum number of spare server processes", + :default => "32" + +attribute "apache/prefork/serverlimit", + :display_name => "Apache Prefork MPM ServerLimit", + :description => "Upper limit on configurable server processes", + :default => "400" + +attribute "apache/prefork/maxclients", + :display_name => "Apache Prefork MPM MaxClients", + :description => "Maximum number of simultaneous connections", + :default => "400" + +attribute "apache/prefork/maxrequestsperchild", + :display_name => "Apache Prefork MPM MaxRequestsPerChild", + :description => "Maximum number of request a child process will handle", + :default => "10000" + +attribute "apache/worker", + :display_name => "Apache Worker", + :description => "Hash of Apache prefork tuning attributes.", + :type => "hash" + +attribute "apache/worker/startservers", + :display_name => "Apache Worker MPM StartServers", + :description => "Initial number of server processes to start", + :default => "4" + +attribute "apache/worker/maxclients", + :display_name => "Apache Worker MPM MaxClients", + :description => "Maximum number of simultaneous connections", + :default => "1024" + +attribute "apache/worker/minsparethreads", + :display_name => "Apache Worker MPM MinSpareThreads", + :description => "Minimum number of spare worker threads", + :default => "64" + +attribute "apache/worker/maxsparethreads", + :display_name => "Apache Worker MPM MaxSpareThreads", + :description => "Maximum number of spare worker threads", + :default => "192" + +attribute "apache/worker/threadsperchild", + :display_name => "Apache Worker MPM ThreadsPerChild", + :description => "Constant number of worker threads in each server process", + :default => "64" + +attribute "apache/worker/maxrequestsperchild", + :display_name => "Apache Worker MPM MaxRequestsPerChild", + :description => "Maximum number of request a child process will handle", + :default => "0" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/default.rb new file mode 100644 index 00000000000..a331546cca0 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/default.rb @@ -0,0 +1,213 @@ +# +# Cookbook Name:: apache2 +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "apache2" do + case node[:platform] + when "centos","redhat","fedora","suse" + package_name "httpd" + when "debian","ubuntu" + package_name "apache2" + when "arch" + package_name "apache" + end + action :install +end + +service "apache2" do + case node[:platform] + when "centos","redhat","fedora","suse" + service_name "httpd" + # If restarted/reloaded too quickly httpd has a habit of failing. + # This may happen with multiple recipes notifying apache to restart - like + # during the initial bootstrap. + restart_command "/sbin/service httpd restart && sleep 1" + reload_command "/sbin/service httpd graceful && sleep 1" + when "debian" + service_name "apache2" + restart_command "/usr/sbin/invoke-rc.d apache2 restart && sleep 1" + reload_command "/usr/sbin/invoke-rc.d apache2 reload && sleep 1" + when "ubuntu" + service_name "apache2" + restart_command "service apache2 restart && sleep 1" + reload_command "service apache2 graceful && sleep 1" + when "arch" + service_name "httpd" + end + supports value_for_platform( + "debian" => { "4.0" => [ :restart, :reload ], "default" => [ :restart, :reload, :status ] }, + "ubuntu" => { "default" => [ :restart, :reload, :status ] }, + "centos" => { "default" => [ :restart, :reload, :status ] }, + "redhat" => { "default" => [ :restart, :reload, :status ] }, + "fedora" => { "default" => [ :restart, :reload, :status ] }, + "arch" => { "default" => [ :restart, :reload, :status ] }, + "default" => { "default" => [:restart, :reload ] } + ) + action :enable +end + +if platform?("centos", "redhat", "fedora", "suse", "arch") + directory node[:apache][:log_dir] do + mode 0755 + action :create + end + + cookbook_file "/usr/local/bin/apache2_module_conf_generate.pl" do + source "apache2_module_conf_generate.pl" + mode 0755 + owner "root" + group "root" + end + + %w{sites-available sites-enabled mods-available mods-enabled}.each do |dir| + directory "#{node[:apache][:dir]}/#{dir}" do + mode 0755 + owner "root" + group "root" + action :create + end + end + + execute "generate-module-list" do + if node[:kernel][:machine] == "x86_64" + libdir = value_for_platform("arch" => { "default" => "lib" }, "default" => "lib64") + else + libdir = "lib" + end + command "/usr/local/bin/apache2_module_conf_generate.pl /usr/#{libdir}/httpd/modules /etc/httpd/mods-available" + action :run + end + + %w{a2ensite a2dissite a2enmod a2dismod}.each do |modscript| + template "/usr/sbin/#{modscript}" do + source "#{modscript}.erb" + mode 0755 + owner "root" + group "root" + end + end + + # installed by default on centos/rhel, remove in favour of mods-enabled + file "#{node[:apache][:dir]}/conf.d/proxy_ajp.conf" do + action :delete + backup false + end + file "#{node[:apache][:dir]}/conf.d/README" do + action :delete + backup false + end + + # welcome page moved to the default-site.rb temlate + file "#{node[:apache][:dir]}/conf.d/welcome.conf" do + action :delete + backup false + end +end + +directory "#{node[:apache][:dir]}/ssl" do + action :create + mode 0755 + owner "root" + group "root" +end + +directory "#{node[:apache][:dir]}/conf.d" do + action :create + mode 0755 + owner "root" + group "root" +end + +directory node[:apache][:cache_dir] do + action :create + mode 0755 + owner node[:apache][:user] +end + +template "apache2.conf" do + case node[:platform] + when "centos","redhat","fedora","arch" + path "#{node[:apache][:dir]}/conf/httpd.conf" + when "debian","ubuntu" + path "#{node[:apache][:dir]}/apache2.conf" + end + source "apache2.conf.erb" + owner "root" + group "root" + mode 0644 + notifies :reload, resources(:service => "apache2") +end + +template "security" do + path "#{node[:apache][:dir]}/conf.d/security" + source "security.erb" + owner "root" + group "root" + mode 0644 + backup false + notifies :reload, resources(:service => "apache2") +end + +template "charset" do + path "#{node[:apache][:dir]}/conf.d/charset" + source "charset.erb" + owner "root" + group "root" + mode 0644 + backup false + notifies :reload, resources(:service => "apache2") +end + +template "#{node[:apache][:dir]}/ports.conf" do + source "ports.conf.erb" + group "root" + owner "root" + variables :apache_listen_ports => node[:apache][:listen_ports] + mode 0644 + notifies :reload, resources(:service => "apache2") +end + +template "#{node[:apache][:dir]}/sites-available/default" do + source "default-site.erb" + owner "root" + group "root" + mode 0644 + notifies :reload, resources(:service => "apache2") +end + +include_recipe "apache2::mod_status" +include_recipe "apache2::mod_alias" +include_recipe "apache2::mod_auth_basic" +include_recipe "apache2::mod_authn_file" +include_recipe "apache2::mod_authz_default" +include_recipe "apache2::mod_authz_groupfile" +include_recipe "apache2::mod_authz_host" +include_recipe "apache2::mod_authz_user" +include_recipe "apache2::mod_autoindex" +include_recipe "apache2::mod_dir" +include_recipe "apache2::mod_env" +include_recipe "apache2::mod_mime" +include_recipe "apache2::mod_negotiation" +include_recipe "apache2::mod_setenvif" +include_recipe "apache2::mod_log_config" if platform?("centos", "redhat", "fedora", "suse", "arch") + +apache_site "default" if platform?("centos", "redhat", "fedora") + +service "apache2" do + action :start +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/god_monitor.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/god_monitor.rb new file mode 100644 index 00000000000..b3c49ebff50 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/god_monitor.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: apache2 +# Recipe:: god_monitor +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_service = service "apache2" do + action :nothing +end + +start_command = apache_service.start_command +stop_command = apache_service.stop_command +restart_command = apache_service.restart_command + +god_monitor "apache2" do + config "apache2.god.erb" + start (start_command)?start_command : "/etc/init.d/#{apache_service.service_name} start" + restart (restart_command)?restart_command : "/etc/init.d/#{apache_service.service_name} restart" + stop (stop_command)?stop_command : "/etc/init.d/#{apache_service.service_name} stop" +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_alias.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_alias.rb new file mode 100644 index 00000000000..a4618ed8d9e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_alias.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: alias +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "alias" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_basic.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_basic.rb new file mode 100644 index 00000000000..d30264f9cb5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_basic.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: auth_basic +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "auth_basic" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_digest.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_digest.rb new file mode 100644 index 00000000000..5aef9260460 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_digest.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: auth_digest +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "auth_digest" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_openid.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_openid.rb new file mode 100644 index 00000000000..cc33eb887f2 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_auth_openid.rb @@ -0,0 +1,83 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mod_auth_openid +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +openid_dev_pkgs = value_for_platform( + "ubuntu" => { "default" => %w{ apache2-prefork-dev libopkele-dev libopkele3 } }, + "debian" => { "default" => %w{ apache2-prefork-dev libopkele-dev libopkele3 } }, + "arch" => { "default" => ["libopkele"] } +) + +case node[:platform] +when "arch" + include_recipe "pacman" + package "tidyhtml" +end + +openid_dev_pkgs.each do |pkg| + case node[:platform] + when "arch" + pacman_aur pkg do + action [:build, :install] + end + else + package pkg + end +end + +remote_file "#{Chef::Config[:file_cache_path]}/mod_auth_openid-0.4.tar.gz" do + source "http://butterfat.net/releases/mod_auth_openid/mod_auth_openid-0.4.tar.gz" + mode 0644 +end + +bash "install mod_auth_openid" do + cwd Chef::Config[:file_cache_path] + code <<-EOH + tar zxvf mod_auth_openid-0.4.tar.gz + cd mod_auth_openid-0.4 && ./configure + perl -pi -e "s/-i -a -n 'authopenid'/-i -n 'authopenid'/g" Makefile + make && make install + EOH + case node[:platform] + when "arch" + not_if { ::File.exists?("/usr/lib/httpd/modules/mod_auth_openid.so") } + else + not_if { ::File.exists?("/usr/lib/apache2/modules/mod_auth_openid.so") } + end +end + +file "#{node[:apache][:cache_dir]}/mod_auth_openid.db" do + owner node[:apache][:user] + mode 0640 +end + +template "#{node[:apache][:dir]}/mods-available/authopenid.load" do + source "mods/authopenid.load.erb" + owner "root" + group "root" + mode 0644 +end + +apache_module "authopenid" + +template "/usr/local/bin/mod_auth_openid.rb" do + source "mod_auth_openid.rb.erb" + owner node[:apache][:user] + group node[:apache][:user] + mode 0750 +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authn_file.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authn_file.rb new file mode 100644 index 00000000000..872caa73ea9 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authn_file.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authn_file +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authn_file" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb new file mode 100644 index 00000000000..0310d2430f7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authnz_ldap.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authnz_ldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authnz_ldap" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_default.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_default.rb new file mode 100644 index 00000000000..123536d04b5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_default" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb new file mode 100644 index 00000000000..b2833b21d08 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_groupfile.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_groupfile +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_groupfile" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_host.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_host.rb new file mode 100644 index 00000000000..87c1a4b0357 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_host.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_host +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_host" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_user.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_user.rb new file mode 100644 index 00000000000..8dd46dfaba4 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_authz_user.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: authz_user +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "authz_user" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_autoindex.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_autoindex.rb new file mode 100644 index 00000000000..622a66e9d05 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_autoindex.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: autoindex +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "autoindex" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_cgi.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_cgi.rb new file mode 100644 index 00000000000..6c15a05386a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_cgi.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: cgi +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "cgi" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dav.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dav.rb new file mode 100644 index 00000000000..fef656ad8ab --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dav.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "dav" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dav_svn.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dav_svn.rb new file mode 100644 index 00000000000..ce50d549d61 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dav_svn.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dav_svn +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "libapache2-svn" + +apache_module "dav_svn" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_deflate.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_deflate.rb new file mode 100644 index 00000000000..b568f305aa9 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_deflate.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: deflate +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "deflate" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dir.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dir.rb new file mode 100644 index 00000000000..9930c3ac73d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_dir.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: dir +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "dir" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_env.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_env.rb new file mode 100644 index 00000000000..d34550312a5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_env.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: env +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "env" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_expires.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_expires.rb new file mode 100644 index 00000000000..9e5042e3636 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_expires.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: expires +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "expires" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_fcgid.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_fcgid.rb new file mode 100644 index 00000000000..8d079f302f0 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_fcgid.rb @@ -0,0 +1,46 @@ +# +# Cookbook Name:: apache2 +# Recipe:: fcgid +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform?("debian", "ubuntu") + package "libapache2-mod-fcgid" +elsif platform?("centos", "redhat", "fedora", "arch") + package "mod_fcgid" do + notifies :run, resources(:execute => "generate-module-list"), :immediately + end + + file "#{node[:apache][:dir]}/conf.d/fcgid.conf" do + action :delete + backup false + end +elsif platform?("suse") + apache_lib_path = node[:architecture] == "i386" ? "/usr/lib/httpd" : "/usr/lib64/httpd" + package "httpd-devel" + bash "install-fcgid" do + code <<-EOH +(cd #{Chef::Config[:file_cache_path]}; wget http://superb-east.dl.sourceforge.net/sourceforge/mod-fcgid/mod_fcgid.2.2.tgz) +(cd #{Chef::Config[:file_cache_path]}; tar zxvf mod_fcgid.2.2.tgz) +(cd #{Chef::Config[:file_cache_path]}; perl -pi -e 's!/usr/local/apache2!#{apache_lib_path}!g' ./mod_fcgid.2.2/Makefile) +(cd #{Chef::Config[:file_cache_path]}/mod_fcgid.2.2; make install) +EOH + end +end + +apache_module "fcgid" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_headers.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_headers.rb new file mode 100644 index 00000000000..5e6b94d1f5e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_headers.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: headers +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "headers" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_ldap.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_ldap.rb new file mode 100644 index 00000000000..0877694a9ab --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_ldap.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: ldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "ldap" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_log_config.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_log_config.rb new file mode 100644 index 00000000000..1a77336d365 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_log_config.rb @@ -0,0 +1,24 @@ +# +# Cookbook Name:: apache2 +# Recipe:: log_config +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform?("centos", "redhat", "fedora", "suse", "arch") + apache_module "log_config" +else + include_recipe "apache2" +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_mime.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_mime.rb new file mode 100644 index 00000000000..16aee1ad6a6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_mime.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: mime +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "mime" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_negotiation.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_negotiation.rb new file mode 100644 index 00000000000..348e11f10de --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_negotiation.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: negotiation +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "negotiation" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_php5.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_php5.rb new file mode 100644 index 00000000000..9950b054490 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_php5.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: apache2 +# Recipe:: php5 +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "debian", "ubuntu" + package "libapache2-mod-php5" do + action :install + end +when "arch" + package "php-apache" do + action :install + notifies :run, resources(:execute => "generate-module-list"), :immediately + end +when "centos", "redhat", "fedora" + package "php" do + action :install + notifies :run, resources(:execute => "generate-module-list"), :immediately + end +end + +apache_module "php5" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy.rb new file mode 100644 index 00000000000..fff7627b663 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb new file mode 100644 index 00000000000..617a2c228d6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_ajp.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_ajp" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb new file mode 100644 index 00000000000..dc62a710d7a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_balancer.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_balancer" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb new file mode 100644 index 00000000000..f41954f136f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_connect.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_connect" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_http.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_http.rb new file mode 100644 index 00000000000..ddff3eaf32b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_proxy_http.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: proxy_http +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "proxy_http" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_python.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_python.rb new file mode 100644 index 00000000000..d524556ea00 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_python.rb @@ -0,0 +1,32 @@ +# +# Cookbook Name:: apache2 +# Recipe:: python +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] + when "debian", "ubuntu" + package "libapache2-mod-python" do + action :install + end + when "centos", "redhat", "fedora" + package "mod_python" do + action :install + notifies :run, resources(:execute => "generate-module-list"), :immediately + end +end + +apache_module "python" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_rewrite.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_rewrite.rb new file mode 100644 index 00000000000..df388a6729b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_rewrite.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: apache2 +# Recipe:: rewrite +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "rewrite" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_setenvif.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_setenvif.rb new file mode 100644 index 00000000000..4048a5f940d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_setenvif.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: setenvif +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "setenvif" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_ssl.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_ssl.rb new file mode 100644 index 00000000000..25f2b8f1281 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_ssl.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: apache2 +# Recipe:: ssl +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if platform?("centos", "redhat", "fedora") + package "mod_ssl" do + action :install + notifies :run, resources(:execute => "generate-module-list"), :immediately + end + + file "#{node[:apache][:dir]}/conf.d/ssl.conf" do + action :delete + backup false + end +end + +ports = node[:apache][:listen_ports].include?("443") ? node[:apache][:listen_ports] : [node[:apache][:listen_ports], "443"].flatten + +template "#{node[:apache][:dir]}/ports.conf" do + source "ports.conf.erb" + variables :apache_listen_ports => ports + notifies :reload, resources(:service => "apache2") + mode 0644 +end + +apache_module "ssl" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_status.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_status.rb new file mode 100644 index 00000000000..3e7172740ec --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_status.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: apache2 +# Recipe:: status +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apache_module "status" do + conf true +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_wsgi.rb b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_wsgi.rb new file mode 100644 index 00000000000..ede0320888a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/recipes/mod_wsgi.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: apache2 +# Recipe:: python +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "debian","ubuntu" + package "libapache2-mod-wsgi" +when "redhat","centos","fedora", "arch" + package "mod_wsgi" +end + +apache_module "wsgi" diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2dismod.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2dismod.erb new file mode 100644 index 00000000000..06fb6b36c38 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2dismod.erb @@ -0,0 +1,22 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which module would you like to disable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/mods-enabled/*.load | \ + sed -e "s,$SYSCONFDIR/mods-enabled/,,g" | sed -e 's/\.load$//g;' | xargs echo + echo -n "Module name? " + read MODNAME +else + MODNAME=$1 +fi + +if ! [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load ]; then + echo "This module is already disabled, or does not exist!" + exit 1 +fi + +rm -f $SYSCONFDIR/mods-enabled/$MODNAME.* +echo "Module $MODNAME disabled; reload apache to fully disable." \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2dissite.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2dissite.erb new file mode 100644 index 00000000000..41feda57f22 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2dissite.erb @@ -0,0 +1,29 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which site would you like to disable?" + echo -n "Your choices are: " + ls $node[:apache][:dir]/sites-enabled/* | \ + sed -e "s,$SYSCONFDIR/sites-enabled/,,g" | xargs echo + echo -n "Site name? " + read SITENAME +else + SITENAME=$1 +fi + +if [ $SITENAME = "default" ]; then + PRIORITY="000" +fi + +if ! [ -e $SYSCONFDIR/sites-enabled/$SITENAME -o \ + -e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" ]; then + echo "This site is already disabled, or does not exist!" + exit 1 +fi + +if ! rm $SYSCONFDIR/sites-enabled/$SITENAME 2>/dev/null; then + rm -f $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" +fi +echo "Site $SITENAME disabled; reload apache to disable." \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2enmod.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2enmod.erb new file mode 100644 index 00000000000..a59c72fe9ab --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2enmod.erb @@ -0,0 +1,37 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which module would you like to enable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/mods-available/*.load | \ + sed -e "s,$SYSCONFDIR/mods-available/,,g" | sed -e 's/\.load$//g;' | xargs echo + echo -n "Module name? " + read MODNAME +else + MODNAME=$1 +fi + +#figure out if we're on a prefork or threaded mpm +if [ -x /usr/sbin/apache2 ]; then + PREFORK=`/usr/sbin/apache2 -l | grep prefork || true` +fi + +if [ -e $SYSCONFDIR/mods-enabled/$MODNAME.load && -e $SYSCONFDIR/mods-enabled/$MODNAME.conf ]; then + echo "This module is already enabled!" + exit 0 +fi + +if ! [ -e $SYSCONFDIR/mods-available/$MODNAME.load ]; then + echo "This module does not exist!" + exit 1 +fi + +for i in conf load; do + if [ -e $SYSCONFDIR/mods-available/$MODNAME.$i -a ! -e $SYSCONFDIR/mods-enabled/$MODNAME.$i ]; then + ln -sf $SYSCONFDIR/mods-available/$MODNAME.$i $SYSCONFDIR/mods-enabled/$MODNAME.$i; + fi +done + +echo "Module $MODNAME installed; reload apache to enable." \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2ensite.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2ensite.erb new file mode 100644 index 00000000000..4288869ffb6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/a2ensite.erb @@ -0,0 +1,38 @@ +#!/bin/sh -e + +SYSCONFDIR='<%= node[:apache][:dir] %>' + +if [ -z $1 ]; then + echo "Which site would you like to enable?" + echo -n "Your choices are: " + ls $SYSCONFDIR/sites-available/* | \ + sed -e "s,$SYSCONFDIR/sites-available/,,g" | xargs echo + echo -n "Site name? " + read SITENAME +else + SITENAME=$1 +fi + +if [ $SITENAME = "default" ]; then + PRIORITY="000" +fi + +if [ -e $SYSCONFDIR/sites-enabled/$SITENAME -o \ + -e $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" ]; then + echo "This site is already enabled!" + exit 0 +fi + +if ! [ -e $SYSCONFDIR/sites-available/$SITENAME ]; then + echo "This site does not exist!" + exit 1 +fi + +if [ $SITENAME = "default" ]; then + ln -sf $SYSCONFDIR/sites-available/$SITENAME \ + $SYSCONFDIR/sites-enabled/"$PRIORITY"-"$SITENAME" +else + ln -sf $SYSCONFDIR/sites-available/$SITENAME $SYSCONFDIR/sites-enabled/$SITENAME +fi + +echo "Site $SITENAME installed; reload apache to enable." \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/apache2.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/apache2.conf.erb new file mode 100644 index 00000000000..02b49a55fa8 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/apache2.conf.erb @@ -0,0 +1,232 @@ +# +# Generated by Chef +# +# Based on the Ubuntu apache2.conf + +ServerRoot "<%= node[:apache][:dir] %>" + +# +# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. +# +<% if node[:platform] == "debian" || node[:platform] == "ubuntu" -%> +LockFile /var/lock/apache2/accept.lock +<% else %> +LockFile logs/accept.lock +<% end -%> + +# +# PidFile: The file in which the server should record its process +# identification number when it starts. +# +<% if node[:platform] == "debian" || node[:platform] == "ubuntu" -%> +PidFile /var/run/apache2.pid +<% elsif node[:platform] == "centos" || node[:platform] == "redhat" || node[:platform] == "fedora" -%> +PidFile /var/run/httpd.pid +<% elsif node[:platform] == "arch" -%> +PidFile /var/run/httpd/httpd.pid +<% else -%> +PidFile logs/httpd.pid +<% end -%> + +# +# Timeout: The number of seconds before receives and sends time out. +# +Timeout <%= node[:apache][:timeout] %> + +# +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. +# +KeepAlive <%= node[:apache][:keepalive] %> + +# +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We recommend you leave this number high, for maximum performance. +# +MaxKeepAliveRequests <%= node[:apache][:keepaliverequests] %> + +# +# KeepAliveTimeout: Number of seconds to wait for the next request from the +# same client on the same connection. +# +KeepAliveTimeout <%= node[:apache][:keepalivetimeout] %> + +## +## Server-Pool Size Regulation (MPM specific) +## + +# prefork MPM +# StartServers: number of server processes to start +# MinSpareServers: minimum number of server processes which are kept spare +# MaxSpareServers: maximum number of server processes which are kept spare +# MaxClients: maximum number of server processes allowed to start +# MaxRequestsPerChild: maximum number of requests a server process serves + + StartServers <%= node[:apache][:prefork][:startservers] %> + MinSpareServers <%= node[:apache][:prefork][:minspareservers] %> + MaxSpareServers <%= node[:apache][:prefork][:maxspareservers] %> + ServerLimit <%= node[:apache][:prefork][:serverlimit] %> + MaxClients <%= node[:apache][:prefork][:maxclients] %> + MaxRequestsPerChild <%= node[:apache][:prefork][:maxrequestsperchild] %> + + +# worker MPM +# StartServers: initial number of server processes to start +# MaxClients: maximum number of simultaneous client connections +# MinSpareThreads: minimum number of worker threads which are kept spare +# MaxSpareThreads: maximum number of worker threads which are kept spare +# ThreadsPerChild: constant number of worker threads in each server process +# MaxRequestsPerChild: maximum number of requests a server process serves + + StartServers <%= node[:apache][:worker][:startservers] %> + MaxClients <%= node[:apache][:worker][:maxclients] %> + MinSpareThreads <%= node[:apache][:worker][:minsparethreads] %> + MaxSpareThreads <%= node[:apache][:worker][:maxsparethreads] %> + ThreadsPerChild <%= node[:apache][:worker][:threadsperchild] %> + MaxRequestsPerChild <%= node[:apache][:worker][:maxrequestsperchild] %> + + +User <%= node[:apache][:user] %> +Group <%= node[:apache][:user] %> + +# +# AccessFileName: The name of the file to look for in each directory +# for additional configuration directives. See also the AllowOverride +# directive. +# + +AccessFileName .htaccess + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Order allow,deny + Deny from all + + +# +# DefaultType is the default MIME type the server will use for a document +# if it cannot otherwise determine one, such as from filename extensions. +# If your server contains mostly text or HTML documents, "text/plain" is +# a good value. If most of your content is binary, such as applications +# or images, you may want to use "application/octet-stream" instead to +# keep browsers from trying to display binary files as though they are +# text. +# +DefaultType text/plain + + +# +# HostnameLookups: Log the names of clients or just their IP addresses +# e.g., www.apache.org (on) or 204.62.129.132 (off). +# The default is off because it'd be overall better for the net if people +# had to knowingly turn this feature on, since enabling it means that +# each client request will result in AT LEAST one lookup request to the +# nameserver. +# +HostnameLookups Off + +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog <%= node[:apache][:log_dir] %>/error.log + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + +# Include module configuration: +Include <%= node[:apache][:dir] %>/mods-enabled/*.load +Include <%= node[:apache][:dir] %>/mods-enabled/*.conf + +# Include ports listing +Include <%= node[:apache][:dir] %>/ports.conf + +# +# The following directives define some format nicknames for use with +# a CustomLog directive (see below). +# +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent +# + +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# Putting this all together, we can internationalize error responses. +# +# We use Alias to redirect any /error/HTTP_.html.var response to +# our collection of by-error message multi-language collections. We use +# includes to substitute the appropriate text. +# +# You can modify the messages' appearance without changing any of the +# default HTTP_.html.var files by adding the line: +# +# Alias /error/include/ "/your/include/path/" +# +# which allows you to create your own set of files by starting with the +# /usr/share/apache2/error/include/ files and copying them to /your/include/path/, +# even on a per-VirtualHost basis. The default include files will display +# your Apache version number and your ServerAdmin email address regardless +# of the setting of ServerSignature. +# +# The internationalized error documents require mod_alias, mod_include +# and mod_negotiation. To activate them, uncomment the following 30 lines. + +# Alias /error/ "/usr/share/apache2/error/" +# +# +# AllowOverride None +# Options IncludesNoExec +# AddOutputFilter Includes html +# AddHandler type-map var +# Order allow,deny +# Allow from all +# LanguagePriority en cs de es fr it nl sv pt-br ro +# ForceLanguagePriority Prefer Fallback +# +# +# ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var +# ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var +# ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var +# ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var +# ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var +# ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var +# ErrorDocument 410 /error/HTTP_GONE.html.var +# ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var +# ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var +# ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var +# ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var +# ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var +# ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var +# ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var +# ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var +# ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var +# ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var + + + +# Include generic snippets of statements +Include <%= node[:apache][:dir] %>/conf.d/ + +# Include the virtual host configurations: +Include <%= node[:apache][:dir] %>/sites-enabled/ diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/apache2.god.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/apache2.god.erb new file mode 100644 index 00000000000..b7315e4b0d2 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/apache2.god.erb @@ -0,0 +1,19 @@ +God.watch do |w| + w.name = "apache2" + w.interval = 30.seconds # default + w.start = "<%= @params[:start] %>" + w.stop = "/etc/init.d/httpd stop" + w.restart = "<%= @params[:restart] %>" + w.start_grace = 10.seconds + w.restart_grace = 10.seconds + w.pid_file = "/var/run/httpd.pid" + w.behavior(:clean_pid_file) + + w.start_if do |start| + start.condition(:process_running) do |c| + c.interval = 5.seconds + c.running = false + c.notify = 'admin' + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/charset.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/charset.erb new file mode 100644 index 00000000000..40d7198b8f3 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/charset.erb @@ -0,0 +1,6 @@ +# Read the documentation before enabling AddDefaultCharset. +# In general, it is only a good idea if you know that all your files +# have this encoding. It will override any encoding given in the files +# in meta http-equiv or xml encoding tags. + +#AddDefaultCharset UTF-8 diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/default-site.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/default-site.erb new file mode 100644 index 00000000000..14b25639c04 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/default-site.erb @@ -0,0 +1,57 @@ + + ServerAdmin <%= node[:apache][:contact] %> + + DocumentRoot /var/www/ + + Options FollowSymLinks + AllowOverride None + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + # This directive allows us to have apache2's default start page + # in /apache2-default/, but still have / go to the right place + #RedirectMatch ^/$ /apache2-default/ + + + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + AllowOverride None + Options ExecCGI -MultiViews +SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + + ErrorLog <%= node[:apache][:log_dir] %>/error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog <%= node[:apache][:log_dir] %>/access.log combined + ServerSignature On + + Alias /doc/ "/usr/share/doc/" + + Options Indexes MultiViews FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all + Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + <% if node[:platform] == "centos" || node[:platform] == "redhat" || node[:platform] == "fedora" -%> + # + # This configuration file enables the default "Welcome" + # page if there is no default index page present for + # the root URL. To disable the Welcome page, comment + # out all the lines below. + # + + Options -Indexes + ErrorDocument 403 /error/noindex.html + + <% end -%> + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mod_auth_openid.rb.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mod_auth_openid.rb.erb new file mode 100644 index 00000000000..51fc1d75214 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mod_auth_openid.rb.erb @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby + +allowed_openids = Array.new +<% node[:apache][:allowed_openids].each do |id| -%> +allowed_openids << "<%= id %>" +<% end -%> + +if allowed_openids.grep(ARGV[0]).length > 0 + exit 0 +else + exit 1 +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/README b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/README new file mode 100644 index 00000000000..df9f0bcc917 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/README @@ -0,0 +1,2 @@ +These configs are taken from a Debian apache2.2-common 2.2.11-3 install. They +work on CentOS 5.3 with a few conditions using erb. diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb new file mode 100644 index 00000000000..45568a57d86 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/alias.conf.erb @@ -0,0 +1,24 @@ + +# +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname +# +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example, only "/icons/". If the fakename is slash-terminated, then the +# realname must also be slash terminated, and if the fakename omits the +# trailing slash, the realname must also omit it. +# +# We include the /icons/ alias for FancyIndexed directory listings. If +# you do not use FancyIndexing, you may comment this out. +# +Alias /icons/ "<%= node[:apache][:icondir] %>" + +"> + Options Indexes MultiViews + AllowOverride None + Order allow,deny + Allow from all + + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/authopenid.load.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/authopenid.load.erb new file mode 100644 index 00000000000..71125085a2b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/authopenid.load.erb @@ -0,0 +1,6 @@ +<% case node[:platform] -%> +<% when "arch" -%> +LoadModule authopenid_module /usr/lib/httpd/modules/mod_auth_openid.so +<% else -%> +LoadModule authopenid_module /usr/lib/apache2/modules/mod_auth_openid.so +<% end -%> diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb new file mode 100644 index 00000000000..38390938ade --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/autoindex.conf.erb @@ -0,0 +1,101 @@ + +# +# Directives controlling the display of server-generated directory listings. +# + +# +# IndexOptions: Controls the appearance of server-generated directory +# listings. +# Remove/replace the "Charset=UTF-8" if you don't use UTF-8 for your filenames. +# +IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* DescriptionWidth=* Charset=UTF-8 + +# +# AddIcon* directives tell the server which icon to show for different +# files or filename extensions. These are only displayed for +# FancyIndexed directories. +# +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip x-bzip2 + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +# It's a suffix rule, so simply matching "core" matches "score" as well ! +AddIcon /icons/bomb.gif /core +AddIcon (SND,/icons/sound2.gif) .ogg +AddIcon (VID,/icons/movie.gif) .ogm + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# Default icons for OpenDocument format +AddIcon /icons/odf6odt-20x22.png .odt +AddIcon /icons/odf6ods-20x22.png .ods +AddIcon /icons/odf6odp-20x22.png .odp +AddIcon /icons/odf6odg-20x22.png .odg +AddIcon /icons/odf6odc-20x22.png .odc +AddIcon /icons/odf6odf-20x22.png .odf +AddIcon /icons/odf6odb-20x22.png .odb +AddIcon /icons/odf6odi-20x22.png .odi +AddIcon /icons/odf6odm-20x22.png .odm + +AddIcon /icons/odf6ott-20x22.png .ott +AddIcon /icons/odf6ots-20x22.png .ots +AddIcon /icons/odf6otp-20x22.png .otp +AddIcon /icons/odf6otg-20x22.png .otg +AddIcon /icons/odf6otc-20x22.png .otc +AddIcon /icons/odf6otf-20x22.png .otf +AddIcon /icons/odf6oti-20x22.png .oti +AddIcon /icons/odf6oth-20x22.png .oth + +# +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. +# +DefaultIcon /icons/unknown.gif + +# +# AddDescription allows you to place a short description after a file in +# server-generated indexes. These are only displayed for FancyIndexed +# directories. +# Format: AddDescription "description" filename +# +#AddDescription "GZIP compressed document" .gz +#AddDescription "tar archive" .tar +#AddDescription "GZIP compressed tar archive" .tgz + +# +# ReadmeName is the name of the README file the server will look for by +# default, and append to directory listings. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. +ReadmeName README.html +HeaderName HEADER.html + +# +# IndexIgnore is a set of filenames which directory indexing should ignore +# and not include in the listing. Shell-style wildcarding is permitted. +# +IndexIgnore .??* *~ *# RCS CVS *,v *,t + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb new file mode 100644 index 00000000000..2e41975fa8e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/deflate.conf.erb @@ -0,0 +1,16 @@ + + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE application/xhtml+xml + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE image/svg+xml + AddOutputFilterByType DEFLATE application/rss+xml + AddOutputFilterByType DEFLATE application/atom_xml + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE application/x-javascript + AddOutputFilterByType DEFLATE application/x-httpd-php + AddOutputFilterByType DEFLATE application/x-httpd-fastphp + AddOutputFilterByType DEFLATE application/x-httpd-eruby + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb new file mode 100644 index 00000000000..e16fcb390d4 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/dir.conf.erb @@ -0,0 +1,5 @@ + + + DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb new file mode 100644 index 00000000000..0e6fc9a8928 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/fcgid.conf.erb @@ -0,0 +1,10 @@ + + AddHandler fcgid-script .fcgi + IPCConnectTimeout 20 + + +<% if node[:platform] == "centos" || node[:platform] == "redhat" || node[:platform] == "fedora" -%> +# Sane place to put sockets and shared memory file +SocketPath run/mod_fcgid +SharememPath run/mod_fcgid/fcgid_shm +<% end -%> diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb new file mode 100644 index 00000000000..958a6b9659e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/mime.conf.erb @@ -0,0 +1,196 @@ + + +# +# TypesConfig points to the file containing the list of mappings from +# filename extension to MIME-type. +# +<% case node[:platform] -%> +<% when "arch" -%> +TypesConfig <%= node['apache']['dir'] %>/conf/mime.types +<% else -%> +TypesConfig /etc/mime.types +<% end -%> + +# +# AddType allows you to add to or override the MIME configuration +# file mime.types for specific file types. +# +#AddType application/x-gzip .tgz +# +# AddEncoding allows you to have certain browsers uncompress +# information on the fly. Note: Not all browsers support this. +# Despite the name similarity, the following Add* directives have +# nothing to do with the FancyIndexing customization directives above. +# +#AddEncoding x-compress .Z +#AddEncoding x-gzip .gz .tgz +#AddEncoding x-bzip2 .bz2 +# +# If the AddEncoding directives above are commented-out, then you +# probably should define those extensions to indicate media types: +# +AddType application/x-compress .Z +AddType application/x-gzip .gz .tgz +AddType application/x-bzip2 .bz2 + +# +# DefaultLanguage and AddLanguage allows you to specify the language of +# a document. You can then use content negotiation to give a browser a +# file in a language the user can understand. +# +# Specify a default language. This means that all data +# going out without a specific language tag (see below) will +# be marked with this one. You probably do NOT want to set +# this unless you are sure it is correct for all cases. +# +# * It is generally better to not mark a page as +# * being a certain language than marking it with the wrong +# * language! +# +# DefaultLanguage nl +# +# Note 1: The suffix does not have to be the same as the language +# keyword --- those with documents in Polish (whose net-standard +# language code is pl) may wish to use "AddLanguage pl .po" to +# avoid the ambiguity with the common suffix for perl scripts. +# +# Note 2: The example entries below illustrate that in some cases +# the two character 'Language' abbreviation is not identical to +# the two character 'Country' code for its country, +# E.g. 'Danmark/dk' versus 'Danish/da'. +# +# Note 3: In the case of 'ltz' we violate the RFC by using a three char +# specifier. There is 'work in progress' to fix this and get +# the reference data for rfc1766 cleaned up. +# +# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) +# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de) +# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja) +# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn) +# Norwegian (no) - Polish (pl) - Portugese (pt) +# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv) +# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW) +# +AddLanguage ca .ca +AddLanguage cs .cz .cs +AddLanguage da .dk +AddLanguage de .de +AddLanguage el .el +AddLanguage en .en +AddLanguage eo .eo +# See README.Debian for Spanish +AddLanguage es .es +AddLanguage et .et +AddLanguage fr .fr +AddLanguage he .he +AddLanguage hr .hr +AddLanguage it .it +AddLanguage ja .ja +AddLanguage ko .ko +AddLanguage ltz .ltz +AddLanguage nl .nl +AddLanguage nn .nn +AddLanguage no .no +AddLanguage pl .po +AddLanguage pt .pt +AddLanguage pt-BR .pt-br +AddLanguage ru .ru +AddLanguage sv .sv +# See README.Debian for Turkish +AddLanguage tr .tr +AddLanguage zh-CN .zh-cn +AddLanguage zh-TW .zh-tw + +# +# Commonly used filename extensions to character sets. You probably +# want to avoid clashes with the language extensions, unless you +# are good at carefully testing your setup after each change. +# See http://www.iana.org/assignments/character-sets for the +# official list of charset names and their respective RFCs. +# +AddCharset us-ascii .ascii .us-ascii +AddCharset ISO-8859-1 .iso8859-1 .latin1 +AddCharset ISO-8859-2 .iso8859-2 .latin2 .cen +AddCharset ISO-8859-3 .iso8859-3 .latin3 +AddCharset ISO-8859-4 .iso8859-4 .latin4 +AddCharset ISO-8859-5 .iso8859-5 .cyr .iso-ru +AddCharset ISO-8859-6 .iso8859-6 .arb .arabic +AddCharset ISO-8859-7 .iso8859-7 .grk .greek +AddCharset ISO-8859-8 .iso8859-8 .heb .hebrew +AddCharset ISO-8859-9 .iso8859-9 .latin5 .trk +AddCharset ISO-8859-10 .iso8859-10 .latin6 +AddCharset ISO-8859-13 .iso8859-13 +AddCharset ISO-8859-14 .iso8859-14 .latin8 +AddCharset ISO-8859-15 .iso8859-15 .latin9 +AddCharset ISO-8859-16 .iso8859-16 .latin10 +AddCharset ISO-2022-JP .iso2022-jp .jis +AddCharset ISO-2022-KR .iso2022-kr .kis +AddCharset ISO-2022-CN .iso2022-cn .cis +AddCharset Big5 .Big5 .big5 .b5 +AddCharset cn-Big5 .cn-big5 +# For russian, more than one charset is used (depends on client, mostly): +AddCharset WINDOWS-1251 .cp-1251 .win-1251 +AddCharset CP866 .cp866 +AddCharset KOI8 .koi8 +AddCharset KOI8-E .koi8-e +AddCharset KOI8-r .koi8-r .koi8-ru +AddCharset KOI8-U .koi8-u +AddCharset KOI8-ru .koi8-uk .ua +AddCharset ISO-10646-UCS-2 .ucs2 +AddCharset ISO-10646-UCS-4 .ucs4 +AddCharset UTF-7 .utf7 +AddCharset UTF-8 .utf8 +AddCharset UTF-16 .utf16 +AddCharset UTF-16BE .utf16be +AddCharset UTF-16LE .utf16le +AddCharset UTF-32 .utf32 +AddCharset UTF-32BE .utf32be +AddCharset UTF-32LE .utf32le +AddCharset euc-cn .euc-cn +AddCharset euc-gb .euc-gb +AddCharset euc-jp .euc-jp +AddCharset euc-kr .euc-kr +#Not sure how euc-tw got in - IANA doesn't list it??? +AddCharset EUC-TW .euc-tw +AddCharset gb2312 .gb2312 .gb +AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2 +AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4 +AddCharset shift_jis .shift_jis .sjis + +# +# AddHandler allows you to map certain file extensions to "handlers": +# actions unrelated to filetype. These can be either built into the server +# or added with the Action directive (see below) +# +# To use CGI scripts outside of ScriptAliased directories: +# (You will also need to add "ExecCGI" to the "Options" directive.) +# +#AddHandler cgi-script .cgi + +# +# For files that include their own HTTP headers: +# +#AddHandler send-as-is asis + +# +# For server-parsed imagemap files: +# +#AddHandler imap-file map + +# +# For type maps (negotiated resources): +# (This is enabled by default to allow the Apache "It Worked" page +# to be distributed in multiple languages.) +# +AddHandler type-map var + +# +# Filters allow you to process content before it is sent to the client. +# +# To parse .shtml files for server-side includes (SSI): +# (You will also need to add "Includes" to the "Options" directive.) +# +AddType text/html .shtml +AddOutputFilter INCLUDES .shtml + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb new file mode 100644 index 00000000000..0e3455bd049 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/negotiation.conf.erb @@ -0,0 +1,18 @@ + +# +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# +# Just list the languages in decreasing order of preference. We have +# more or less alphabetized them here. You probably want to change this. +# +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv tr zh-CN zh-TW + +# +# ForceLanguagePriority allows you to serve a result page rather than +# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback) +# [in case no accepted languages matched the available variants] +# +ForceLanguagePriority Prefer Fallback + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb new file mode 100644 index 00000000000..46407a1e592 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/proxy.conf.erb @@ -0,0 +1,19 @@ + + #turning ProxyRequests on and allowing proxying from all may allow + #spammers to use your proxy to send email. + + ProxyRequests Off + + + AddDefaultCharset off + Order deny,allow + Deny from all + #Allow from .example.com + + + # Enable/disable the handling of HTTP/1.1 "Via:" headers. + # ("Full" adds the server version; "Block" removes all outgoing Via: headers) + # Set to one of: Off | On | Full | Block + + ProxyVia On + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb new file mode 100644 index 00000000000..6b7d6e2f6cd --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/setenvif.conf.erb @@ -0,0 +1,28 @@ + + +# +# The following directives modify normal HTTP response behavior to +# handle known problems with browser implementations. +# +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# The following directive disables redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with Microsoft WebFolders which does not appropriately handle +# redirects for folders with DAV methods. +# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. +# +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "MS FrontPage" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully +BrowserMatch "^gnome-vfs/1.0" redirect-carefully +BrowserMatch "^XML Spy" redirect-carefully +BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb new file mode 100644 index 00000000000..3b7735f666f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/ssl.conf.erb @@ -0,0 +1,72 @@ + +# +# Pseudo Random Number Generator (PRNG): +# Configure one or more sources to seed the PRNG of the SSL library. +# The seed data should be of good random quality. +# WARNING! On some platforms /dev/random blocks if not enough entropy +# is available. This means you then cannot use the /dev/random device +# because it would lead to very long connection times (as long as +# it requires to make more entropy available). But usually those +# platforms additionally provide a /dev/urandom device which doesn't +# block. So, if available, use this one instead. Read the mod_ssl User +# Manual for more details. +# +SSLRandomSeed startup builtin +SSLRandomSeed startup file:/dev/urandom 512 +SSLRandomSeed connect builtin +SSLRandomSeed connect file:/dev/urandom 512 + +## +## SSL Global Context +## +## All SSL configuration in this context applies both to +## the main server and all SSL-enabled virtual hosts. +## + +# +# Some MIME-types for downloading Certificates and CRLs +# +AddType application/x-x509-ca-cert .crt +AddType application/x-pkcs7-crl .crl + +# Pass Phrase Dialog: +# Configure the pass phrase gathering process. +# The filtering dialog program (`builtin' is a internal +# terminal dialog) has to provide the pass phrase on stdout. +SSLPassPhraseDialog builtin + +# Inter-Process Session Cache: +# Configure the SSL Session Cache: First the mechanism +# to use and second the expiring timeout (in seconds). +#SSLSessionCache dbm:/var/run/apache2/ssl_scache +<% if node[:platform] == "centos" || node[:platform] == "redhat" || node[:platform] == "fedora" -%> +SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) +<% else -%> +SSLSessionCache shmcb:/var/run/apache2/ssl_scache +<% end -%> +SSLSessionCacheTimeout 300 + +# Semaphore: +# Configure the path to the mutual exclusion semaphore the +# SSL engine uses internally for inter-process synchronization. +<% if node[:platform] == "centos" || node[:platform] == "redhat" || node[:platform] == "fedora" -%> +SSLMutex default +<% else -%> +SSLMutex file:/var/run/apache2/ssl_mutex +<% end -%> + +# SSL Cipher Suite: +# List the ciphers that the client is permitted to negotiate. +# See the mod_ssl documentation for a complete list. +# enable only secure ciphers: +SSLCipherSuite HIGH:MEDIUM:!ADH +# Use this instead if you want to allow cipher upgrades via SGC facility. +# In this case you also have to use something like +# SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 +# see http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html.en#upgradeenc +#SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + +# enable only secure protocols: SSLv3 and TLSv1, but not SSLv2 +SSLProtocol all -SSLv2 + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/status.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/status.conf.erb new file mode 100644 index 00000000000..679d111563e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/mods/status.conf.erb @@ -0,0 +1,16 @@ + +# +# Allow server status reports generated by mod_status, +# with the URL of http://servername/server-status +# Uncomment and change the ".example.com" to allow +# access from other hosts. +# + + SetHandler server-status + Order deny,allow + Deny from all + Allow from localhost ip6-localhost +# Allow from .example.com + + + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/port_apache.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/port_apache.erb new file mode 100644 index 00000000000..f6078dd08d6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/port_apache.erb @@ -0,0 +1,2 @@ +# Port <%= @port %> +-A FWR -p tcp -m tcp --dport <%= @port %> -j ACCEPT \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/ports.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/ports.conf.erb new file mode 100644 index 00000000000..cc3631e1185 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/ports.conf.erb @@ -0,0 +1,6 @@ +#This file generated via template by Chef. +<% @apache_listen_ports.each do |port| -%> +Listen <%= port %> +NameVirtualHost *:<%= port %> + +<% end -%> diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/security.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/security.erb new file mode 100644 index 00000000000..70c97ad3773 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/security.erb @@ -0,0 +1,50 @@ +# +# Disable access to the entire file system except for the directories that +# are explicitly allowed later. +# +# This currently breaks the configurations that come with some web application +# Debian packages. It will be made the default for the release after lenny. +# +# +# AllowOverride None +# Order Deny,Allow +# Deny from all +# + + +# Changing the following options will not really affect the security of the +# server, but might make attacks slightly more difficult in some cases. + +# +# ServerTokens +# This directive configures what you return as the Server HTTP response +# Header. The default is 'Full' which sends information about the OS-Type +# and compiled in modules. +# Set to one of: Full | OS | Minimal | Minor | Major | Prod +# where Full conveys the most information, and Prod the least. +# +#ServerTokens Minimal +ServerTokens <%= node[:apache][:servertokens] %> + +# +# Optionally add a line containing the server version and virtual host +# name to server-generated pages (internal error documents, FTP directory +# listings, mod_status and mod_info output etc., but not CGI generated +# documents or custom error documents). +# Set to "EMail" to also include a mailto: link to the ServerAdmin. +# Set to one of: On | Off | EMail +# +#ServerSignature Off +ServerSignature <%= node[:apache][:serversignature] %> + +# +# Allow TRACE method +# +# Set to "extended" to also reflect the request body (only for testing and +# diagnostic purposes). +# +# Set to one of: On | Off | extended +# +#TraceEnable Off +TraceEnable <%= node[:apache][:traceenable] %> + diff --git a/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/web_app.conf.erb b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/web_app.conf.erb new file mode 100644 index 00000000000..6be3fc984f8 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apache2/templates/default/web_app.conf.erb @@ -0,0 +1,43 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + RewriteEngine On + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + + Options FollowSymLinks + AllowOverride None + + + + SetHandler server-status + + Order Deny,Allow + Deny from all + Allow from 127.0.0.1 + + + LogLevel info + ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + + RewriteEngine On + RewriteLog <%= node[:apache][:log_dir] %>/<%= @application_name %>-rewrite.log + RewriteLogLevel 0 + + # Canonical host, <%= @params[:server_name] %> + RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC] + RewriteCond %{HTTP_HOST} !^$ + RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301] + + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/README.md b/crowbar/change-image/dell/chef/cookbooks/apt/README.md new file mode 100644 index 00000000000..2d31122f8d2 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/README.md @@ -0,0 +1,109 @@ +Description +=========== + +Configures various APT components on Debian-like systems. Also includes a LWRP. + +Recipes +======= + +default +------- +The default recipe runs apt-get update during the Compile Phase of the Chef run to ensure that the system's package cache is updated with the latest. It is recommended that this recipe appear first in a node's run list (directly or through a role) to ensure that when installing packages, Chef will be able to download the latest version available on the remote APT repository. + +This recipe also sets up a local cache directory for preseeding packages. + +cacher +------ +Installs the apt-cacher package and service so the system can provide APT caching. You can check the usage report at http://{hostname}:3142/report. The cacher recipe includes the `cacher-client` recipe, so it helps seed itself. + +cacher-client +------------- +Configures the node to use the apt-cacher server as a client. + +Resources/Providers +=================== + +This LWRP provides an easy way to manage additional APT repositories. + +# Actions + +- :add: creates a repository file and builds the repository listing +- :remove: removes the repository file + +# Attribute Parameters + +- repo_name: name attribute. The name of the channel to discover +- uri: the base of the Debian distribution +- distribution: this is usually your release's codename...ie something like `karmic`, `lucid` or `maverick` +- components: package groupings..when it doubt use `main` +- deb_src: whether or not to add the repository as a source repo as well +- key_server: the GPG keyserver where the key for the repo should be retrieved +- key: if a `key_server` is provided, this is assumed to be the fingerprint, otherwise it is the URI to the GPG key for the repo + +# Example + + # add the Zenoss repo + apt_repository "zenoss" do + uri "http://dev.zenoss.org/deb" + components ["main","stable"] + action :add + end + + # add the Nginx PPA; grab key from keyserver + apt_repository "nginx-php" do + uri "http://ppa.launchpad.net/nginx/php5/ubuntu" + distribution node['lsb']['codename'] + components ["main"] + keyserver "keyserver.ubuntu.com" + key "C300EE8C" + action :add + end + + # add the Cloudkick Repo + apt_repository "cloudkick" do + uri "http://packages.cloudkick.com/ubuntu" + distribution node['lsb']['codename'] + components ["main"] + key "http://packages.cloudkick.com/cloudkick.packages.key" + action :add + end + + # remove Zenoss repo + apt_repository "zenoss" do + action :remove + end + +Usage +===== + +Put `recipe[apt]` first in the run list. If you have other recipes that you want to use to configure how apt behaves, like new sources, notify the execute resource to run, e.g.: + + template "/etc/apt/sources.list.d/my_apt_sources.list" do + notifies :run, resources(:execute => "apt-get update"), :immediately + end + +The above will run during execution phase since it is a normal template resource, and should appear before other package resources that need the sources in the template. + +Put `recipe[apt::cacher]` in the run_list for a server to provide APT caching and add `recipe[apt::cacher-client]` on the rest of the Debian-based nodes to take advantage of the caching server. + +License and Author +================== + +Author:: Joshua Timberman () +Author:: Matt Ray () +Author:: Seth Chisamore () + +Copyright 2009-2011 Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-cacher b/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-cacher new file mode 100644 index 00000000000..dab948852ab --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-cacher @@ -0,0 +1,9 @@ +# apt-cacher startup configuration file + +# IMPORTANT: check the apt-cacher.conf file before using apt-cacher as daemon. + +# set to 1 to start the daemon at boot time +AUTOSTART=1 + +# extra settings to override the ones in apt-cacher.conf +# EXTRAOPT=" daemon_port=3142 limit=30 " diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-cacher.conf b/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-cacher.conf new file mode 100644 index 00000000000..32ca3c32d5e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-cacher.conf @@ -0,0 +1,144 @@ +# This file has been modified by ./apt-proxy-to-apt-cacher +# Some lines may have been appended at the bottom of this file +# This file has been modified by /usr/share/apt-cacher/apt-proxy-to-apt-cacher +# Some lines may have been appended at the bottom of this file +################################################################# +# This is the config file for apt-cacher. On most Debian systems +# you can safely leave the defaults alone. +################################################################# + +# cache_dir is used to set the location of the local cache. This can +# become quite large, so make sure it is somewhere with plenty of space. +cache_dir=/var/cache/apt-cacher + +# The email address of the administrator is displayed in the info page +# and traffic reports. +admin_email=root@localhost + +# For the daemon startup settings please edit the file /etc/default/apt-cacher. + +# Daemon port setting, only useful in stand-alone mode. You need to run the +# daemon as root to use privileged ports (<1024). +daemon_port = 3142 + +# optional settings, user and group to run the daemon as. Make sure they have +# sufficient permissions on the cache and log directories. Comment the settings +# to run apt-cacher as the native user. +group=www-data +user=www-data + +# optional setting, binds the listening daemon to one specified IP. Use IP +# ranges for more advanced configuration, see below. +# daemon_addr=localhost + +# If your apt-cacher machine is directly exposed to the Internet and you are +# worried about unauthorised machines fetching packages through it, you can +# specify a list of IPv4 addresses which are allowed to use it and another +# list of IPv4 addresses which aren't. +# Localhost (127.0.0.1) is always allowed. Other addresses must be matched +# by allowed_hosts and not by denied_hosts to be permitted to use the cache. +# Setting allowed_hosts to "*" means "allow all". +# Otherwise the format is a comma-separated list containing addresses, +# optionally with masks (like 10.0.0.0/22), or ranges of addresses (two +# addresses separated by a hyphen, no masks, like '192.168.0.3-192.168.0.56'). +allowed_hosts=* +denied_hosts= + +# And similiarly for IPv6 with allowed_hosts_6 and denied_hosts_6. +# Note that IPv4-mapped IPv6 addresses (::ffff:w.x.y.z) are truncated to +# w.x.y.z and are handled as IPv4. +allowed_hosts_6=fec0::/16 +denied_hosts_6= + +# This thing can be done by Apache but is much simplier here - limit access to +# Debian mirrors based on server names in the URLs +#allowed_locations=ftp.uni-kl.de,ftp.nerim.net,debian.tu-bs.de + +# Apt-cacher can generate usage reports every 24 hours if you set this +# directive to 1. You can view the reports in a web browser by pointing +# to your cache machine with '/apt-cacher/report' on the end, like this: +# http://yourcache.example.com/apt-cacher/report +# Generating reports is very fast even with many thousands of logfile +# lines, so you can safely turn this on without creating much +# additional system load. +generate_reports=1 + +# Apt-cacher can clean up its cache directory every 24 hours if you set +# this directive to 1. Cleaning the cache can take some time to run +# (generally in the order of a few minutes) and removes all package +# files that are not mentioned in any existing 'Packages' lists. This +# has the effect of deleting packages that have been superseded by an +# updated 'Packages' list. +clean_cache=1 + +# The directory to use for apt-cacher access and error logs. +# The access log records every request in the format: +# date-time|client ip address|HIT/MISS/EXPIRED|object size|object name +# The error log is slightly more free-form, and is also used for debug +# messages if debug mode is turned on. +# Note that the old 'logfile' and 'errorfile' directives are +# deprecated: if you set them explicitly they will be honoured, but it's +# better to just get rid of them from old config files. +logdir=/var/log/apt-cacher + +# apt-cacher can use different methods to decide whether package lists need to +# be updated, +# A) looking at the age of the cached files +# B) getting HTTP header from server and comparing that with cached data. This +# method is more reliable and avoids desynchronisation of data and index files +# but needs to transfer few bytes from the server every time somebody requests +# the files ("apt-get update") +# Set the following value to the maximum age (in hours) for method A or to 0 +# for method B +expire_hours=0 + +# Apt-cacher can pass all its requests to an external http proxy like +# Squid, which could be very useful if you are using an ISP that blocks +# port 80 and requires all web traffic to go through its proxy. The +# format is 'hostname:port', eg: 'proxy.example.com:8080'. +http_proxy=proxy.example.com:8080 + +# Use of an external proxy can be turned on or off with this flag. +# Value should be either 0 (off) or 1 (on). +use_proxy=0 + +# External http proxy sometimes need authentication to get full access. The +# format is 'username:password'. +http_proxy_auth=proxyuser:proxypass + +# Use of external proxy authentication can be turned on or off with this flag. +# Value should be either 0 (off) or 1 (on). +use_proxy_auth=0 + +# Rate limiting sets the maximum bandwidth in bytes per second to use +# for fetching packages. Syntax is fully defined in 'man wget'. +# Use 'k' or 'm' to use kilobits or megabits / second: eg, 'limit=25k'. +# Use 0 or a negative value for no rate limiting. +limit=0 + +# Debug mode makes apt-cacher spew a lot of extra debug junk to the +# error log (whose location is defined with the 'logdir' directive). +# Leave this off unless you need it, or your error log will get very +# big. Acceptable values are 0 or 1. +debug=0 + +# Adapt the line in the usage info web page to match your server configuration +# example_sources_line=deb http://my.cacher.server:3142/ftp.au.debian.org/debian unstable main contrib non-free + +# Print a 410 (Gone) HTTP message with the specified text when accessed via +# CGI. Useful to tell users to adapt their sources.list files when the +# apt-cacher server is beeing relocated (via apt-get's error messages while +# running "update") +#cgi_advise_to_use = Please use http://cacheserver:3142/ as apt-cacher access URL +#cgi_advise_to_use = Server relocated. To change sources.list, run perl -pe "s,/apt-cacher\??,:3142," -i /etc/apt/sources.list + +# Server mapping - this allows to hide real server names behind virtual paths +# that appear in the access URL. This method is known from apt-proxy. This is +# also the only method to use FTP access to the target hosts. The syntax is simple, the part of the beginning to replace, followed by a list of mirror urls, all space separated. Multiple profile are separated by semicolons +# path_map = debian ftp.uni-kl.de/pub/linux/debian ftp2.de.debian.org/debian ; ubuntu archive.ubuntu.com/ubuntu ; security security.debian.org/debian-security ftp2.de.debian.org/debian-security +# Note that you need to specify all target servers in the allowed_locations +# options if you make use of it. Also note that the paths should not overlap +# each other. FTP access method not supported yet, maybe in the future. + +# extra setting from apt-proxy configuration +path_map = ubuntu us.archive.ubuntu.com/ubuntu ; ubuntu-security security.ubuntu.com/ubuntu ; debian debian.osuosl.org/debian/ ; security security.debian.org/debian-security diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-proxy-v2.conf b/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-proxy-v2.conf new file mode 100644 index 00000000000..6541f25ed05 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/files/default/apt-proxy-v2.conf @@ -0,0 +1,50 @@ +[DEFAULT] +;; All times are in seconds, but you can add a suffix +;; for minutes(m), hours(h) or days(d) + +;; commented out address so apt-proxy will listen on all IPs +;; address = 127.0.0.1 +port = 9999 +cache_dir = /var/cache/apt-proxy + +;; Control files (Packages/Sources/Contents) refresh rate +min_refresh_delay = 1s +complete_clientless_downloads = 1 + +;; Debugging settings. +debug = all:4 db:0 + +time = 30 +passive_ftp = on + +;;-------------------------------------------------------------- +;; Cache housekeeping + +cleanup_freq = 1d +max_age = 120d +max_versions = 3 + +;;--------------------------------------------------------------- +;; Backend servers +;; +;; Place each server in its own [section] + +[ubuntu] +; Ubuntu archive +backends = + http://us.archive.ubuntu.com/ubuntu + +[ubuntu-security] +; Ubuntu security updates +backends = http://security.ubuntu.com/ubuntu + +[debian] +;; Backend servers, in order of preference +backends = + http://debian.osuosl.org/debian/ + +[security] +;; Debian security archive +backends = + http://security.debian.org/debian-security + http://ftp2.de.debian.org/debian-security diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/apt/metadata.rb new file mode 100644 index 00000000000..2c3af4056b7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/metadata.rb @@ -0,0 +1,13 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures apt and apt services and an LWRP for managing apt repositories" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.1.1" +recipe "apt", "Runs apt-get update during compile phase and sets up preseed directories" +recipe "apt::cacher", "Set up an APT cache" +recipe "apt::cacher-client", "Client for the apt::cacher server" + +%w{ ubuntu debian }.each do |os| + supports os +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/providers/repository.rb b/crowbar/change-image/dell/chef/cookbooks/apt/providers/repository.rb new file mode 100644 index 00000000000..397f34170d5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/providers/repository.rb @@ -0,0 +1,72 @@ +# +# Cookbook Name:: apt +# Provider:: repository +# +# Copyright 2010-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :add do + unless ::File.exists?("/etc/apt/sources.list.d/#{new_resource.repo_name}-source.list") + Chef::Log.info "Adding #{new_resource.repo_name} repository to /etc/apt/sources.list.d/#{new_resource.repo_name}-source.list" + # add key + if new_resource.keyserver && new_resource.key + execute "install-key #{new_resource.key}" do + command "apt-key adv --keyserver #{new_resource.keyserver} --recv #{new_resource.key}" + action :nothing + end.run_action(:run) + elsif new_resource.key && (new_resource.key =~ /http/) + key_name = new_resource.key.split(/\//).last + remote_file "#{Chef::Config[:file_cache_path]}/#{key_name}" do + source new_resource.key + mode "0644" + action :nothing + end.run_action(:create_if_missing) + execute "install-key #{key_name}" do + command "apt-key add #{Chef::Config[:file_cache_path]}/#{key_name}" + action :nothing + end.run_action(:run) + end + # build our listing + repository = "deb" + repository = "deb-src" if new_resource.deb_src + repository = "# Created by the Chef apt_repository LWRP\n" + repository + repository += " #{new_resource.uri}" + repository += " #{new_resource.distribution}" + new_resource.components.each {|component| repository += " #{component}"} + # write out the file, replace it if it already exists + file "/etc/apt/sources.list.d/#{new_resource.repo_name}-source.list" do + owner "root" + group "root" + mode 0644 + content repository + "\n" + action :nothing + end.run_action(:create) + execute "update package index" do + command "apt-get update" + action :nothing + end.run_action(:run) + new_resource.updated_by_last_action(true) + end +end + +action :remove do + if ::File.exists?("/etc/apt/sources.list.d/#{new_resource.repo_name}-source.list") + Chef::Log.info "Removing #{new_resource.repo_name} repository from /etc/apt/sources.list.d/" + file "/etc/apt/sources.list.d/#{new_resource.repo_name}-source.list" do + action :delete + end + new_resource.updated_by_last_action(true) + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/recipes/cacher-client.rb b/crowbar/change-image/dell/chef/cookbooks/apt/recipes/cacher-client.rb new file mode 100644 index 00000000000..b173b8b2abd --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/recipes/cacher-client.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: apt +# Recipe:: cacher-client +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +servers = search(:node, 'recipes:apt\:\:cacher') || [] +if servers.length > 0 + Chef::Log.info("apt-cacher server found on #{servers[0]}.") + proxy = "Acquire::http::Proxy \"http://#{servers[0].ipaddress}:3142\";" + file "/etc/apt/apt.conf.d/01proxy" do + owner "root" + group "root" + mode "0644" + content proxy + action :create + end +else + Chef::Log.info("No apt-cacher server found.") + file "/etc/apt/apt.conf.d/01proxy" do + action :delete + only_if {File.exists?("/etc/apt/apt.conf.d/01proxy")} + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/recipes/cacher.rb b/crowbar/change-image/dell/chef/cookbooks/apt/recipes/cacher.rb new file mode 100644 index 00000000000..3d4a086f3e1 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/recipes/cacher.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: apt +# Recipe:: cacher +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "apt-cacher" do + action :install +end + +service "apt-cacher" do + supports :restart => true, :status => false + action [ :enable, :start ] +end + +cookbook_file "/etc/apt-cacher/apt-cacher.conf" do + source "apt-cacher.conf" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apt-cacher") +end + +cookbook_file "/etc/default/apt-cacher" do + source "apt-cacher" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "apt-cacher") +end + +#this will help seed the proxy +include_recipe "apt::cacher-client" diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/apt/recipes/default.rb new file mode 100644 index 00000000000..d1117a7b9b5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/recipes/default.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: apt +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +e = execute "apt-get update" do + action :nothing +end + +e.run_action(:run) + +%w{/var/cache/local /var/cache/local/preseeding}.each do |dirname| + directory dirname do + owner "root" + group "root" + mode 0755 + action :create + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/apt/resources/repository.rb b/crowbar/change-image/dell/chef/cookbooks/apt/resources/repository.rb new file mode 100644 index 00000000000..1b36734d7e0 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/apt/resources/repository.rb @@ -0,0 +1,30 @@ +# +# Cookbook Name:: apt +# Resource:: repository +# +# Copyright 2010-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :remove + +#name of the repo, used for source.list filename +attribute :repo_name, :kind_of => String, :name_attribute => true +attribute :uri, :kind_of => String +attribute :distribution, :kind_of => String +attribute :components, :kind_of => Array, :default => [] +#whether or not to add the repository as a source repo as well +attribute :deb_src, :default => false +attribute :keyserver, :kind_of => String, :default => nil +attribute :key, :kind_of => String, :default => nil diff --git a/crowbar/change-image/dell/chef/cookbooks/build-essential/README.md b/crowbar/change-image/dell/chef/cookbooks/build-essential/README.md new file mode 100644 index 00000000000..d75724da37a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/build-essential/README.md @@ -0,0 +1,24 @@ +DESCRIPTION +=========== + +Installs packages required for compiling C software from source. + +LICENSE AND AUTHOR +================== + +Author:: Joshua Timberman () +Author:: Seth Chisamore () + +Copyright 2009-2011, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/build-essential/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/build-essential/metadata.rb new file mode 100644 index 00000000000..e32d66883ef --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/build-essential/metadata.rb @@ -0,0 +1,10 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs C compiler / build tools" +version "1.0.0" +recipe "build-essential", "Installs C compiler and build tools on Linux" + +%w{ fedora redhat centos ubuntu debian }.each do |os| + supports os +end diff --git a/crowbar/change-image/dell/chef/cookbooks/build-essential/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/build-essential/recipes/default.rb new file mode 100644 index 00000000000..ccb95018e1b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/build-essential/recipes/default.rb @@ -0,0 +1,45 @@ +# +# Cookbook Name:: build-essential +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node['platform'] +when "ubuntu","debian" + %w{build-essential binutils-doc}.each do |pkg| + package pkg do + action :install + end + end +when "centos","redhat","fedora" + %w{gcc gcc-c++ kernel-devel make}.each do |pkg| + package pkg do + action :install + end + end +end + +package "autoconf" do + action :install +end + +package "flex" do + action :install +end + +package "bison" do + action :install +end diff --git a/crowbar/change-image/dell/chef/cookbooks/minicom/files/default/minirc.switch b/crowbar/change-image/dell/chef/cookbooks/minicom/files/default/minirc.switch new file mode 100644 index 00000000000..979138d845e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/minicom/files/default/minirc.switch @@ -0,0 +1,5 @@ +# Machine-generated file - use setup menu in minicom to change parameters. +pu port /dev/ttyS0 +pu baudrate 9600 +pu minit +pu rtscts No diff --git a/crowbar/change-image/dell/chef/cookbooks/minicom/metadata.json b/crowbar/change-image/dell/chef/cookbooks/minicom/metadata.json new file mode 100644 index 00000000000..8eae7025778 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/minicom/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "minicom": "Installs minicom with switch access file" + }, + "maintainer_email": "crowbar@dell.com", + "name": "minicom", + "conflicting": { + }, + "description": "Installs minicom with switch access file", + "long_description": "" + } diff --git a/crowbar/change-image/dell/chef/cookbooks/minicom/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/minicom/recipes/default.rb new file mode 100644 index 00000000000..ba70c7fa4b8 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/minicom/recipes/default.rb @@ -0,0 +1,7 @@ + +package "minicom" + +cookbook_file "/etc/minicom/minirc.switch" do + source "minirc.switch" +end + diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/README.md b/crowbar/change-image/dell/chef/cookbooks/mysql/README.md new file mode 100644 index 00000000000..b4f63fbcbd5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/README.md @@ -0,0 +1,113 @@ +Description +=========== + +Installs and configures MySQL client or server. + +Requirements +============ + +Platform +-------- + +* Debian, Ubuntu +* CentOS, Red Hat, Fedora + +Cookbooks +--------- + +Requires Opscode's openssl cookbook for secure password generation. + +Requires a C compiler and Ruby development package in order to build mysql gem with native extensions. On Debian and Ubuntu systems this is satisfied by installing the "build-essential" and "ruby-dev" packages before running Chef. See USAGE below for information on how to handle this during a Chef run. + +Resources and Providers +======================= + +The cookbook contains a LWRP, `mysql_database` which can be used to manage databases through calls to the MySQL API. The mysql gem is installed to make this usable. The provider currently supports three actions: + +* `flush_tables_with_read_lock` - sends the sql command "flush tables with read lock", used for setting up mysql master/slave replication. +* `unflush_tables` - sends the sql command "unflush tables", used for setting up master/slave replication. +* `create_db` - specify a database to be created. +* `query` - send an arbitrary query to the database, this should be used with care. Pass the SQL statement to use with the `sql` resource attribute. + +For example see the USAGE section below. + +Attributes +========== + +* `mysql[:server_root_password]` - Set the server's root password with this, default is a randomly generated password with `OpenSSL::Random.random_bytes`. +* `mysql[:server_repl_password]` - Set the replication user 'repl' password with this, default is a randomly generated password with `OpenSSL::Random.random_bytes`. +* `mysql[:server_debian_password]` - Set the debian-sys-maint user password with this, default is a randomly generated password with `OpenSSL::Random.random_bytes`. +* `mysql[:bind_address]` - Listen address for MySQLd, default is node's ipaddress. +* `mysql[:datadir]` - Location for mysql data directory, default is "/var/lib/mysql" +* `mysql[:ec2_path]` - location of mysql datadir on EC2 nodes, default "/mnt/mysql" + +Performance tuning attributes, each corresponds to the same-named parameter in my.cnf; default values listed + +* `mysql[:tunable][:key_buffer]` = "250M" +* `mysql[:tunable][:max_connections]` = "800" +* `mysql[:tunable][:wait_timeout]` = "180" +* `mysql[:tunable][:net_write_timeout]` = "30" +* `mysql[:tunable][:net_write_timeout]` = "30" +* `mysql[:tunable][:back_log]` = "128" +* `mysql[:tunable][:table_cache]` = "128" +* `mysql[:tunable][:max_heap_table_size]` = "32M" + +Usage +===== + +On client nodes, + + include_recipe "mysql::client" + +This will install the MySQL client libraries and development headers on the system. It will also install the Ruby Gem `mysql`, so that the cookbook's LWRP (above) can be used. This is done during the compile-phase of the Chef run. On platforms that are known to have a native package (currently Debian, Ubuntu, Red hat, Centos, Fedora and SUSE), the package will be installed. Other platforms will use the RubyGem. + +This creates a resource object for the package and does the installation before other recipes are parsed. You'll need to have the C compiler and such (ie, build-essential on Ubuntu) before running the recipes, but we already do that when installing Chef :-). If you want to be able to access a MySQL database via Ruby within another recipe, you could do so, like so: + + mysql_database "create application_production database" do + host "localhost" + username "root" + password node[:mysql][:server_root_password] + database "application_production" + action :create_db + end + +This will connect to the MySQL server running on localhost as "root" and password as `mysql[:server_root_password]` attribute (see below) and create the database specified with the `database` parameter. The provider will attempt to determine whether the database exists first. + +On server nodes, + + include_recipe "mysql::server" + +On Debian and Ubuntu, this will preseed the mysql-server package with the randomly generated root password from the attributes file. On other platforms, it simply installs the required packages. It will also create an SQL file, /etc/mysql/grants.sql, that will be used to set up grants for the root, repl and debian-sys-maint users. + +On EC2 nodes, + + include_recipe "mysql::server_ec2" + +When the `ec2_path` doesn't exist we look for a mounted filesystem (eg, EBS) and move the datadir there. + +The client recipe is already included by server and 'default' recipes. + +For more infromation on the compile vs execution phase of a Chef run: + +* http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run + +License and Author +================== + +Author:: Joshua Timberman () +Author:: AJ Christensen () +Author:: Seth Chisamore () + +Copyright:: 2009-2011 Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/attributes/server.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/attributes/server.rb new file mode 100644 index 00000000000..33c4b79f030 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/attributes/server.rb @@ -0,0 +1,62 @@ +# +# Cookbook Name:: mysql +# Attributes:: server +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['mysql']['bind_address'] = ipaddress +default['mysql']['datadir'] = "/var/lib/mysql" + +case node["platform"] +when "centos", "redhat", "fedora", "suse" + set['mysql']['socket'] = "/var/lib/mysql/mysql.sock" + set['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + set['mysql']['old_passwords'] = 1 +else + set['mysql']['socket'] = "/var/run/mysqld/mysqld.sock" + set['mysql']['pid_file'] = "/var/run/mysqld/mysqld.pid" + set['mysql']['old_passwords'] = 0 +end + +if attribute?('ec2') + default['mysql']['ec2_path'] = "/mnt/mysql" + default['mysql']['ebs_vol_dev'] = "/dev/sdi" + default['mysql']['ebs_vol_size'] = 50 +end + +default['mysql']['tunable']['back_log'] = "128" +default['mysql']['tunable']['key_buffer'] = "256M" +default['mysql']['tunable']['max_allowed_packet'] = "16M" +default['mysql']['tunable']['max_connections'] = "800" +default['mysql']['tunable']['max_heap_table_size'] = "32M" +default['mysql']['tunable']['myisam_recover'] = "BACKUP" +default['mysql']['tunable']['net_read_timeout'] = "30" +default['mysql']['tunable']['net_write_timeout'] = "30" +default['mysql']['tunable']['table_cache'] = "128" +default['mysql']['tunable']['table_open_cache'] = "128" +default['mysql']['tunable']['thread_cache'] = "128" +default['mysql']['tunable']['thread_cache_size'] = 8 +default['mysql']['tunable']['thread_concurrency'] = 10 +default['mysql']['tunable']['thread_stack'] = "256K" +default['mysql']['tunable']['wait_timeout'] = "180" + +default['mysql']['tunable']['query_cache_limit'] = "1M" +default['mysql']['tunable']['query_cache_size'] = "16M" + +default['mysql']['tunable']['log_slow_queries'] = "/var/log/mysql/slow.log" +default['mysql']['tunable']['long_query_time'] = 2 + +default['mysql']['tunable']['innodb_buffer_pool_size'] = "256M" diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/libraries/database.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/libraries/database.rb new file mode 100644 index 00000000000..3403e254a6e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/libraries/database.rb @@ -0,0 +1,19 @@ +begin + require 'mysql' +rescue LoadError + Chef::Log.info("Missing gem 'mysql'") +end + +module Opscode + module Mysql + module Database + def db + @db ||= ::Mysql.new new_resource.host, new_resource.username, new_resource.password + end + def close + @db.close rescue nil + @db = nil + end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/metadata.rb new file mode 100644 index 00000000000..70d011d2b52 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/metadata.rb @@ -0,0 +1,77 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures mysql for client or server" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "1.0.3" +recipe "mysql", "Includes the client recipe to configure a client" +recipe "mysql::client", "Installs packages required for mysql clients using run_action magic" +recipe "mysql::server", "Installs packages required for mysql servers w/o manual intervention" +recipe "mysql::server_ec2", "Performs EC2-specific mountpoint manipulation" + +%w{ debian ubuntu centos suse fedora redhat }.each do |os| + supports os +end + +depends "openssl" + +attribute "mysql/server_root_password", + :display_name => "MySQL Server Root Password", + :description => "Randomly generated password for the mysqld root user", + :default => "randomly generated" + +attribute "mysql/bind_address", + :display_name => "MySQL Bind Address", + :description => "Address that mysqld should listen on", + :default => "ipaddress" + +attribute "mysql/datadir", + :display_name => "MySQL Data Directory", + :description => "Location of mysql databases", + :default => "/var/lib/mysql" + +attribute "mysql/ec2_path", + :display_name => "MySQL EC2 Path", + :description => "Location of mysql directory on EC2 instance EBS volumes", + :default => "/mnt/mysql" + +attribute "mysql/tunable", + :display_name => "MySQL Tunables", + :description => "Hash of MySQL tunable attributes", + :type => "hash" + +attribute "mysql/tunable/key_buffer", + :display_name => "MySQL Tuntable Key Buffer", + :default => "250M" + +attribute "mysql/tunable/max_connections", + :display_name => "MySQL Tunable Max Connections", + :default => "800" + +attribute "mysql/tunable/wait_timeout", + :display_name => "MySQL Tunable Wait Timeout", + :default => "180" + +attribute "mysql/tunable/net_read_timeout", + :display_name => "MySQL Tunable Net Read Timeout", + :default => "30" + +attribute "mysql/tunable/net_write_timeout", + :display_name => "MySQL Tunable Net Write Timeout", + :default => "30" + +attribute "mysql/tunable/back_log", + :display_name => "MySQL Tunable Back Log", + :default => "128" + +attribute "mysql/tunable/table_cache", + :display_name => "MySQL Tunable Table Cache for MySQL < 5.1.3", + :default => "128" + +attribute "mysql/tunable/table_open_cache", + :display_name => "MySQL Tunable Table Cache for MySQL >= 5.1.3", + :default => "128" + +attribute "mysql/tunable/max_heap_table_size", + :display_name => "MySQL Tunable Max Heap Table Size", + :default => "32M" diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/providers/database.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/providers/database.rb new file mode 100644 index 00000000000..36f5ca0a8a4 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/providers/database.rb @@ -0,0 +1,81 @@ +# +# Cookbook Name:: mysql +# Provider:: database +# +# Copyright:: 2008-2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include Opscode::Mysql::Database + +action :flush_tables_with_read_lock do + if exists? + begin + Chef::Log.info "mysql_database: flushing tables with read lock" + db.query "flush tables with read lock" + new_resource.updated_by_last_action(true) + ensure + db.close + end + end +end + +action :unflush_tables do + if exists? + begin + Chef::Log.info "mysql_database: unlocking tables" + db.query "unlock tables" + new_resource.updated_by_last_action(true) + ensure + db.close + end + end +end + +action :create_db do + unless exists? + begin + Chef::Log.info "mysql_database: Creating database #{new_resource.database}" + db.query("create database #{new_resource.database}") + new_resource.updated_by_last_action(true) + ensure + db.close + end + end +end + +action :query do + if exists? + begin + Chef::Log.info "mysql_database: Performing Query: #{new_resource.sql}" + db.query(new_resource.sql) + new_resource.updated_by_last_action(true) + ensure + db.close + end + end +end + +def load_current_resource + Gem.clear_paths + require 'mysql' + + @mysqldb = Chef::Resource::MysqlDatabase.new(new_resource.name) + @mysqldb.database(new_resource.database) +end + +private +def exists? + db.list_dbs.include?(new_resource.database) +end diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/client.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/client.rb new file mode 100644 index 00000000000..c8e0b52e82c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/client.rb @@ -0,0 +1,54 @@ +# +# Cookbook Name:: mysql +# Recipe:: client +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "mysql-devel" do + package_name value_for_platform( + [ "centos", "redhat", "suse", "fedora"] => { "default" => "mysql-devel" }, + ["debian", "ubuntu"] => { "default" => 'libmysqlclient-dev' }, + "default" => 'libmysqlclient-dev' + ) + action :install +end + +package "mysql-client" do + package_name value_for_platform( + [ "centos", "redhat", "suse", "fedora"] => { "default" => "mysql" }, + "default" => "mysql-client" + ) + action :install +end + +if platform?(%w{debian ubuntu redhat centos fedora suse}) + + package "mysql-ruby" do + package_name value_for_platform( + [ "centos", "redhat", "suse", "fedora"] => { "default" => "ruby-mysql" }, + ["debian", "ubuntu"] => { "default" => 'libmysql-ruby' }, + "default" => 'libmysql-ruby' + ) + action :install + end + +else + + gem_package "mysql" do + action :install + end + +end diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/default.rb new file mode 100644 index 00000000000..9ff90d6ca3c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "mysql::client" diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/server.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/server.rb new file mode 100644 index 00000000000..33f28f4d334 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/server.rb @@ -0,0 +1,129 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, Opscode::OpenSSL::Password) + +include_recipe "mysql::client" + +# generate all passwords +node.set_unless['mysql']['server_debian_password'] = secure_password +node.set_unless['mysql']['server_root_password'] = secure_password +node.set_unless['mysql']['server_repl_password'] = secure_password + +if platform?(%w{debian ubuntu}) + + directory "/var/cache/local/preseeding" do + owner "root" + group "root" + mode 0755 + recursive true + end + + execute "preseed mysql-server" do + command "debconf-set-selections /var/cache/local/preseeding/mysql-server.seed" + action :nothing + end + + template "/var/cache/local/preseeding/mysql-server.seed" do + source "mysql-server.seed.erb" + owner "root" + group "root" + mode "0600" + notifies :run, resources(:execute => "preseed mysql-server"), :immediately + end + + template "/etc/mysql/debian.cnf" do + source "debian.cnf.erb" + owner "root" + group "root" + mode "0600" + end + +end + +package "mysql-server" do + action :install +end + +service "mysql" do + service_name value_for_platform([ "centos", "redhat", "suse", "fedora" ] => {"default" => "mysqld"}, "default" => "mysql") + if (platform?("ubuntu") && node.platform_version.to_f >= 10.04) + restart_command "restart mysql" + stop_command "stop mysql" + start_command "start mysql" + end + supports :status => true, :restart => true, :reload => true + action :nothing +end + +template value_for_platform([ "centos", "redhat", "suse" , "fedora" ] => {"default" => "/etc/my.cnf"}, "default" => "/etc/mysql/my.cnf") do + source "my.cnf.erb" + owner "root" + group "root" + mode "0644" + notifies :restart, resources(:service => "mysql"), :immediately +end + +unless Chef::Config[:solo] + ruby_block "save node data" do + block do + node.save + end + action :create + end +end + +# set the root password on platforms +# that don't support pre-seeding +unless platform?(%w{debian ubuntu}) + + execute "assign-root-password" do + command "/usr/bin/mysqladmin -u root password #{node['mysql']['server_root_password']}" + action :run + only_if "/usr/bin/mysql -u root -e 'show databases;'" + end + +end + +grants_path = value_for_platform( + ["centos", "redhat", "suse", "fedora" ] => { + "default" => "/etc/mysql_grants.sql" + }, + "default" => "/etc/mysql/grants.sql" +) + +begin + t = resources("template[/etc/mysql/grants.sql]") +rescue + Chef::Log.info("Could not find previously defined grants.sql resource") + t = template "/etc/mysql/grants.sql" do + path grants_path + source "grants.sql.erb" + owner "root" + group "root" + mode "0600" + action :create + end +end + +execute "mysql-install-privileges" do + command "/usr/bin/mysql -u root #{node['mysql']['server_root_password'].empty? ? '' : '-p' }#{node['mysql']['server_root_password']} < #{grants_path}" + action :nothing + subscribes :run, resources("template[/etc/mysql/grants.sql]"), :immediately +end diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/server_ec2.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/server_ec2.rb new file mode 100644 index 00000000000..d7cce51fd02 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/recipes/server_ec2.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: mysql +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +if (node.attribute?('ec2') && ! FileTest.directory?(node['mysql']['ec2_path'])) + + service "mysql" do + action :stop + end + + execute "install-mysql" do + command "mv #{node['mysql']['datadir']} #{node['mysql']['ec2_path']}" + not_if do FileTest.directory?(node['mysql']['ec2_path']) end + end + + directory node['mysql']['ec2_path'] do + owner "mysql" + group "mysql" + end + + mount node['mysql']['datadir'] do + device node['mysql']['ec2_path'] + fstype "none" + options "bind,rw" + action :mount + end + + service "mysql" do + action :start + end + +end + diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/resources/database.rb b/crowbar/change-image/dell/chef/cookbooks/mysql/resources/database.rb new file mode 100644 index 00000000000..1bb02f93064 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/resources/database.rb @@ -0,0 +1,27 @@ +# +# Cookbook Name:: mysql +# Resource:: database +# +# Copyright:: 2008-2011, Opscode, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :flush_tables_with_read_lock, :unflush_tables, :create_db, :query + +attribute :host, :kind_of => String +attribute :username, :kind_of => String +attribute :password, :kind_of => String +attribute :database, :kind_of => String +attribute :sql, :kind_of => String +attribute :exists, :default => false diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/debian.cnf.erb b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/debian.cnf.erb new file mode 100644 index 00000000000..989b1257dad --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/debian.cnf.erb @@ -0,0 +1,12 @@ +[client] +host = localhost +user = debian-sys-maint +password = <%= node['mysql']['server_debian_password'] %> +socket = <%= node['mysql']['socket'] %> + +[mysql_upgrade] +host = localhost +user = debian-sys-maint +password = <%= node['mysql']['server_debian_password'] %> +socket = <%= node['mysql']['socket'] %> +basedir = /usr diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/grants.sql.erb b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/grants.sql.erb new file mode 100644 index 00000000000..da06c04a6cd --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/grants.sql.erb @@ -0,0 +1,12 @@ +# Generated by Chef for <%= node['hostname'] %>. +# Local modifications will be overwritten. + +<% case node['platform'] -%> +<% when "debian","ubuntu" -%> +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY '<%= node[:mysql][:server_debian_password] %>' WITH GRANT OPTION; +<% end -%> +# Grant replication for a slave user. +GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' identified by '<%= node['mysql']['server_repl_password'] %>'; + +# Set the server root password. This should be preseeded by the package installation. +SET PASSWORD FOR 'root'@'localhost' = PASSWORD('<%= node['mysql']['server_root_password'] %>'); diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/my.cnf.erb b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/my.cnf.erb new file mode 100644 index 00000000000..6703b151b8c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/my.cnf.erb @@ -0,0 +1,173 @@ +# +# Generated by Chef for <%= node['hostname'] %> +# +# Local modifications will be overwritten. +# +# The MySQL database server configuration file. +# +# You can copy this to one of: +# - "/etc/mysql/my.cnf" to set global options, +# - "~/.my.cnf" to set user-specific options. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +# Remember to edit /etc/mysql/debian.cnf when changing the socket location. +[client] +port = 3306 +socket = <%= node['mysql']['socket'] %> + +# Here is entries for some specific programs +# The following values assume you have at least 32M ram + +# This was formally known as [safe_mysqld]. Both versions are currently parsed. +[mysqld_safe] +socket = <%= node['mysql']['socket'] %> +nice = 0 + +[mysqld] +# +# * Basic Settings +# + +# +# * IMPORTANT +# If you make changes to these settings and your system uses apparmor, you may +# also need to also adjust /etc/apparmor.d/usr.sbin.mysqld. +# + +user = mysql +pid-file = <%= node['mysql']['pid_file'] %> +socket = <%= node['mysql']['socket'] %> +port = 3306 +basedir = /usr +datadir = <%= node['mysql']['datadir'] %> +tmpdir = /tmp +skip-external-locking +# +# Instead of skip-networking the default is now to listen only on +# localhost which is more compatible and is not less secure. +bind-address = <%= node['mysql']['bind_address'] %> +# +# * Fine Tuning +# +key_buffer = <%= node['mysql']['tunable']['key_buffer'] %> +max_allowed_packet = 16M +thread_stack = 128K +thread_cache_size = 8 +# This replaces the startup script and checks MyISAM tables if needed +# the first time they are touched +myisam-recover = BACKUP +#max_connections = 100 +#table_cache = 64 +#thread_concurrency = 10 +max_connections = <%= node['mysql']['tunable']['max_connections'] %> +wait_timeout = <%= node['mysql']['tunable']['wait_timeout'] %> +net_read_timeout = <%= node['mysql']['tunable']['net_read_timeout'] %> +net_write_timeout = <%= node['mysql']['tunable']['net_write_timeout'] %> +back_log = <%= node['mysql']['tunable']['back_log'] %> +table_cache = <%= node['mysql']['tunable']['table_cache'] %> +max_heap_table_size = <%= node['mysql']['tunable']['max_heap_table_size'] %> + +# +# * Query Cache Configuration +# +query_cache_limit = 1M +query_cache_size = 16M +# +# * Logging and Replication +# +# Both location gets rotated by the cronjob. +# Be aware that this log type is a performance killer. +#log = /var/log/mysql/mysql.log +# +# Error logging goes to syslog. This is a Debian improvement :) +# +# Here you can see queries with especially long duration +log_slow_queries = <%= node['mysql']['tunable']['log_slow_queries'] %> +long_query_time = <%= node['mysql']['tunable']['long_query_time'] %> +log-queries-not-using-indexes +# +# The following can be used as easy to replay backup logs or for replication. +# note: if you are setting up a replication slave, see README.Debian about +# other settings you may need to change. +#server-id = 1 +#log_bin = /var/log/mysql/mysql-bin.log +expire_logs_days = 10 +max_binlog_size = 100M +#binlog_do_db = include_database_name +#binlog_ignore_db = include_database_name +# +# * InnoDB +# +# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. +# Read the manual for more InnoDB related options. There are many! +# You might want to disable InnoDB to shrink the mysqld process by circa 100MB. +#skip-innodb +innodb_buffer_pool_size = <%= node['mysql']['tunable']['innodb_buffer_pool_size'] %> +# +# * Federated +# +# The FEDERATED storage engine is disabled since 5.0.67 by default in the .cnf files +# shipped with MySQL distributions (my-huge.cnf, my-medium.cnf, and so forth). +# +skip-federated +# +# * Security Features +# +# Read the manual, too, if you want chroot! +# chroot = /var/lib/mysql/ +# +# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". +# +# ssl-ca=/etc/mysql/cacert.pem +# ssl-cert=/etc/mysql/server-cert.pem +# ssl-key=/etc/mysql/server-key.pem + +[mysqldump] +quick +quote-names +max_allowed_packet = 16M + +[mysql] +#no-auto-rehash # faster start of mysql but no tab completition + +[isamchk] +key_buffer = 16M + +# +# * NDB Cluster +# +# See /usr/share/doc/mysql-server-*/README.Debian for more information. +# +# The following configuration is read by the NDB Data Nodes (ndbd processes) +# not from the NDB Management Nodes (ndb_mgmd processes). +# +# [MYSQL_CLUSTER] +# ndb-connectstring=127.0.0.1 + +<% case node['platform'] -%> +<% when "centos", "redhat", "fedora", "suse" -%> +# +# * BerkeleyDB +# +# Using BerkeleyDB is now discouraged as its support will cease in 5.1.12. +skip-bdb +# Default to using old password format for compatibility with mysql 3.x +# clients (those using the mysqlclient10 compatibility package). +old_passwords = <%= node['mysql']['old_passwords'] %> + +<% else -%> +# +# * IMPORTANT: Additional settings that can override those from this file! +# The files must end with '.cnf', otherwise they'll be ignored. +# +!includedir /etc/mysql/conf.d/ +<% end -%> diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/mysql-server.seed.erb b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/mysql-server.seed.erb new file mode 100644 index 00000000000..a5a74f0b3c8 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/mysql-server.seed.erb @@ -0,0 +1,10 @@ +mysql-server-5.0 mysql-server/root_password_again select <%= node['mysql']['server_root_password'] %> +mysql-server-5.0 mysql-server/root_password select <%= node['mysql']['server_root_password'] %> +mysql-server-5.0 mysql-server-5.0/really_downgrade boolean false +mysql-server-5.0 mysql-server-5.0/need_sarge_compat boolean false +mysql-server-5.0 mysql-server-5.0/start_on_boot boolean true +mysql-server-5.0 mysql-server/error_setting_password boolean false +mysql-server-5.0 mysql-server-5.0/nis_warning note +mysql-server-5.0 mysql-server-5.0/postrm_remove_databases boolean false +mysql-server-5.0 mysql-server/password_mismatch boolean false +mysql-server-5.0 mysql-server-5.0/need_sarge_compat_done boolean true diff --git a/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/port_mysql.erb b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/port_mysql.erb new file mode 100644 index 00000000000..55a2ffccf04 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/mysql/templates/default/port_mysql.erb @@ -0,0 +1,3 @@ +# MySQL +-A FWR -p tcp -m tcp --dport 3306 -j ACCEPT +-A FWR -p udp -m udp --dport 3306 -j ACCEPT \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/nscd/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/nscd/metadata.rb new file mode 100644 index 00000000000..d0e321e7b7f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/nscd/metadata.rb @@ -0,0 +1,12 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures nscd" +version "0.7" +suggests "openldap" + +recipe "nscd", "Installs and configures nscd" + +%w{ redhat centos debian ubuntu }.each do |os| + supports os +end diff --git a/crowbar/change-image/dell/chef/cookbooks/nscd/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/nscd/recipes/default.rb new file mode 100644 index 00000000000..fca30f20e2e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/nscd/recipes/default.rb @@ -0,0 +1,33 @@ +# +# Cookbook Name:: nscd +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +package "nscd" do + action :upgrade +end + +service "nscd" do + supports :restart => true, :status => true + action [:enable, :start] +end + +%w{ passwd group }.each do |cmd| + execute "nscd-clear-#{cmd}" do + command "/usr/sbin/nscd -i #{cmd}" + action :nothing + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/README.rdoc b/crowbar/change-image/dell/chef/cookbooks/openldap/README.rdoc new file mode 100644 index 00000000000..161b76a36b9 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/README.rdoc @@ -0,0 +1,101 @@ += DESCRIPTION: + +Configures a server to be an OpenLDAP master, OpenLDAP replication slave, or OpenLDAP client. + += REQUIREMENTS: + +== Platform: + +Ubuntu 8.10 was primarily used in testing this cookbook. Other Ubuntu versions and Debian may work. CentOS and Red Hat are not fully supported, but we take patches. + +== Recipes: + +* openssh +* nscd + += ATTRIBUTES: + +Be aware of the attributes used by this cookbook and adjust the defaults for your environment where required, in attributes/openldap.rb. + +== Client node attributes + +* openldap[:basedn] - basedn +* openldap[:server] - the LDAP server fully qualified domain name, default 'ldap'.node[:domain]. + +== Server node attributes + +* openldap[:slapd_type] - master | slave +* openldap[:slapd_rid] - unique integer ID, required if type is slave. +* openldap[:slapd_master] - hostname of slapd master, attempts to search for slapd_type master. + +== Apache configuration attributes + +Attributes useful for Apache authentication with LDAP. + +COOK-128 - set automatically based on openldap[:server] and openldap[:basedn] if those attributes are set. openldap[:auth_bindpw] remains nil by default as a default value is not easily predicted. + +* openldap[:auth_type] - determine whether binddn and bindpw are required (openldap no, ad yes) +* openldap[:auth_url] - AuthLDAPURL +* openldap[:auth_binddn] - AuthLDAPBindDN +* openldap[:auth_bindpw] - AuthLDAPBindPassword + += USAGE: + +Edit Rakefile variables for SSL certificate. + +On client systems, + + include_recipe "openldap::auth" + +This will get the required packages and configuration for client systems. This will be required on server systems as well, so this is a good candidate for inclusion in a site-cookbooks/base. + +On server systems, set the server node attributes in the Chef node, or in a JSON attributes file. Include the openldap::server recipe: + + include_recipe "openldap::server" + +When initially installing a brand new LDAP master server on Ubuntu 8.10, the configuration directory may need to be removed and recreated before slapd will start successfully. Doing this programmatically may cause other issues, so fix the directory manually :-). + + $ sudo slaptest -F /etc/ldap/slapd.d + str2entry: invalid value for attributeType objectClass #1 (syntax 1.3.6.1.4.1.1466.115.121.1.38) + => ldif_enum_tree: failed to read entry for /etc/ldap/slapd.d/cn=config/olcDatabase={1}bdb.ldif + slaptest: bad configuration directory! + +Simply remove the configuration, rerun chef-client. For some reason slapd isn't getting started even though the service resource is notified to start, so start it manually. + + $ sudo rm -rf /etc/ldap/slapd.d/ /etc/ldap/slapd.conf + $ sudo chef-client + $ sudo /etc/init.d/slapd start + +=== A note about certificates + +Certificates created by the Rakefile are self signed. If you have a purchased CA, that can be used. Be sure to update the certificate locations in the templates as required. We suggest copying this cookbook to the site-cookbooks for such modifications, so you can still pull from our master for updates, and then merge your changes in. + +== NEW DIRECTORY: + +If installing for the first time, the initial directory needs to be created. Create an ldif file, and start populating the directory. + +== PASSWORDS: + +Set the password, openldap[:rootpw] for the rootdn in the node's attributes. This should be a password hash generated from slappasswd. The default slappasswd command on Ubuntu 8.10 and Mac OS X 10.5 will generate a SHA1 hash: + + $ slappasswd -s "secretsauce" + {SSHA}6BjlvtSbVCL88li8IorkqMSofkLio58/ + +Set this by default in the attributes file, or on the node's entry in the webui. + +== LICENSE & AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/Rakefile b/crowbar/change-image/dell/chef/cookbooks/openldap/Rakefile new file mode 100644 index 00000000000..5a8ddfa6d92 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/Rakefile @@ -0,0 +1,48 @@ +# Rakefile for OpenLDAP cookbook. +# Primarily used for generating SSL certificate(s). +# Extend with other OpenLDAP related tasks as required. + +require 'tempfile' + +COMPANY_NAME = "Company" +SSL_COUNTRY_NAME = "US" +SSL_STATE_NAME = "State" +SSL_LOCALITY_NAME = "City" +SSL_ORGANIZATIONAL_UNIT_NAME = "Operations" +SSL_EMAIL_ADDRESS = "operations@example.com" +CADIR = File.expand_path(File.join(File.dirname(__FILE__), "files", "default", "ssl")) + +desc "Create a new self-signed SSL certificate for FQDN=foo.example.com" +task :ssl_cert do + $expect_verbose = true + fqdn = ENV["FQDN"] + fqdn =~ /^(.+?)\.(.+)$/ + hostname = $1 + domain = $2 + raise "Must provide FQDN!" unless fqdn && hostname && domain + puts "** Creating self signed SSL Certificate for #{fqdn}" + sh("(cd #{CADIR} && openssl genrsa 2048 > #{fqdn}.key)") + sh("(cd #{CADIR} && chmod 644 #{fqdn}.key)") + puts "* Generating Self Signed Certificate Request" + tf = Tempfile.new("#{fqdn}.ssl-conf") + ssl_config = < #{fqdn}.crt)") + sh("(cd #{CADIR} && openssl x509 -noout -fingerprint -text < #{fqdn}.crt > #{fqdn}.info)") + sh("(cd #{CADIR} && cat #{fqdn}.crt #{fqdn}.key > #{fqdn}.pem)") + sh("(cd #{CADIR} && chmod 644 #{fqdn}.pem)") +end \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/openldap/attributes/default.rb new file mode 100644 index 00000000000..4d8585ea52f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/attributes/default.rb @@ -0,0 +1,61 @@ +# Cookbook Name:: openldap +# Attributes:: openldap +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if domain.length > 0 + default[:openldap][:basedn] = "dc=#{domain.split('.').join(",dc=")}" + default[:openldap][:server] = "ldap.#{domain}" +end + +openldap[:rootpw] = nil + +# File and directory locations for openldap. +case platform +when "redhat","centos" + set[:openldap][:dir] = "/etc/openldap" + set[:openldap][:run_dir] = "/var/run/openldap" + set[:openldap][:module_dir] = "/usr/lib64/openldap" +when "debian","ubuntu" + set[:openldap][:dir] = "/etc/ldap" + set[:openldap][:run_dir] = "/var/run/slapd" + set[:openldap][:module_dir] = "/usr/lib/ldap" +else + set[:openldap][:dir] = "/etc/ldap" + set[:openldap][:run_dir] = "/var/run/slapd" + set[:openldap][:module_dir] = "/usr/lib/ldap" +end + +openldap[:ssl_dir] = "#{openldap[:dir]}/ssl" +openldap[:cafile] = "#{openldap[:ssl_dir]}/ca.crt" + +# Server settings. +openldap[:slapd_type] = nil + +if openldap[:slapd_type] == "slave" + master = search(:nodes, 'openldap_slapd_type:master') + default[:openldap][:slapd_master] = master + default[:openldap][:slapd_replpw] = nil + default[:openldap][:slapd_rid] = 102 +end + +# Auth settings for Apache. +if openldap[:basedn] && openldap[:server] + default[:openldap][:auth_type] = "openldap" + default[:openldap][:auth_binddn] = "ou=people,#{openldap[:basedn]}" + default[:openldap][:auth_bindpw] = nil + default[:openldap][:auth_url] = "ldap://#{openldap[:server]}/#{openldap[:auth_binddn]}?uid?sub?(objecctClass=*)" +end diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-account b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-account new file mode 100644 index 00000000000..c840654c24e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-account @@ -0,0 +1,7 @@ +# Generated by Chef. Local modifications will be overwritten. +# +# /etc/pam.d/common-account - authorization settings common to all services +# +account sufficient pam_unix.so +account [default=bad success=ok user_unknown=ignore] pam_ldap.so + diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-auth b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-auth new file mode 100644 index 00000000000..cc1b137f6a2 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-auth @@ -0,0 +1,7 @@ +# Generated by Chef. Local modifications will be overwritten. +# +# /etc/pam.d/common-auth - authentication settings common to all services +# +auth sufficient pam_unix.so likeauth nullok_secure +auth sufficient pam_ldap.so use_first_pass + diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-password b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-password new file mode 100644 index 00000000000..7657f2030f6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-password @@ -0,0 +1,7 @@ +# Generated by Chef. Local modifications will be overwritten. +# +# /etc/pam.d/common-password - password-related modules common to all services +# +password sufficient pam_unix.so nullok obscure min=8 max=8 md5 +password sufficient pam_ldap.so + diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-session b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-session new file mode 100644 index 00000000000..518180f1891 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/common-session @@ -0,0 +1,9 @@ +# Generated by Chef. Local modifications will be overwritten. +# +# /etc/pam.d/common-session - session-related modules common to all services +# +session required pam_unix.so +session required pam_mkhomedir.so skel=/etc/skel/ +session required pam_ldap.so +#session optional pam_foreground.so + diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/nsswitch.conf b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/nsswitch.conf new file mode 100644 index 00000000000..dd16cd46d8b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/nsswitch.conf @@ -0,0 +1,21 @@ +# Generated by Chef. Local modifications will be overwritten. +# +# /etc/nsswitch.conf +# +# Example configuration of GNU Name Service Switch functionality. +# If you have the `glibc-doc-reference' and `info' packages installed, try: +# `info libc "Name Service Switch"' for information about this file. + +passwd: files ldap +group: files ldap +shadow: files ldap + +hosts: files dns +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/slapd.seed b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/slapd.seed new file mode 100644 index 00000000000..5949f66bab3 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/files/default/slapd.seed @@ -0,0 +1,21 @@ +slapd slapd/password1 password +slapd slapd/internal/adminpw password +slapd slapd/password2 password +slapd slapd/allow_ldap_v2 boolean false +slapd slapd/password_mismatch note +slapd slapd/suffix_change boolean false +slapd slapd/fix_directory boolean true +slapd slapd/invalid_config boolean true +slapd slapd/slave_databases_require_updateref note +slapd shared/organization string monkey +slapd slapd/upgrade_slapcat_failure note +slapd slapd/dump_database_destdir string /var/backups/slapd-VERSION +slapd slapd/autoconf_modules boolean true +slapd slapd/purge_database boolean false +slapd slapd/domain string monkey.com +slapd slapd/backend select BDB +slapd slapd/no_configuration boolean false +slapd slapd/migrate_ldbm_to_bdb boolean true +slapd slapd/move_old_database boolean true +slapd slapd/dump_database select when needed +slapd slapd/upgrade_slapadd_failure note diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/openldap/metadata.rb new file mode 100644 index 00000000000..948b83367fd --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/metadata.rb @@ -0,0 +1,99 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Configures a server to be an OpenLDAP master, replication slave or client for auth" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.9.4" +recipe "openldap", "Empty, use one of the other recipes" +recipe "openldap::auth", "Set up openldap for user authentication" +recipe "openldap::client", "Install openldap client packages" +recipe "openldap::server", "Set up openldap to be a slapd server" + +%w{ ubuntu debian }.each do |os| + supports os +end + +%w{ openssh nscd }.each do |cb| + depends cb +end + +attribute "openldap/basedn", + :display_name => "OpenLDAP BaseDN", + :description => "BaseDN for the LDAP directory", + :default => "dc=domain,dc=com" + +attribute "openldap/server", + :display_name => "OpenLDAP Server", + :description => "LDAP Server, used for URIs", + :default => "ldap.domain" + +attribute "openldap/rootpw", + :display_name => "OpenLDAP Root Password", + :description => "Password for 'admin' root user, should be a SHA hash that OpenLDAP supports", + :default => "nil" + +attribute "openldap/dir", + :display_name => "OpenLDAP Dir", + :description => "Main configuration directory for OpenLDAP", + :default => "/etc/ldap" + +attribute "openldap/run_dir", + :display_name => "OpenLDAP Run Directory", + :description => "Run directory for LDAP server processes", + :default => "/var/run/slapd" + +attribute "openldap/module_dir", + :display_name => "OpenLDAP Module Directory", + :description => "Location for OpenLDAP add-on modules", + :default => "/usr/lib/ldap" + +attribute "openldap/ssl_dir", + :display_name => "OpenLDAP SSL Directory", + :description => "Location for LDAP SSL certificates", + :default => "openldap_dir/ssl" + +attribute "openldap/cafile", + :display_name => "OpenLDAP CA File", + :description => "Location for CA certificate", + :default => "openldap_dir_ssl/ca.crt" + +attribute "openldap/slapd_type", + :display_name => "OpenLDAP Slapd Type", + :description => "Whether the server is a master or slave", + :default => "nil" + +attribute "openldap/slapd_master", + :display_name => "OpenLDP Slapd Master", + :description => "Search nodes for attribute slapd_type master, for slaves", + :default => "nil" + +attribute "openldap/slapd_replpw", + :display_name => "OpenLDAP Slapd Replication Password", + :description => "Password for slaves to replicate from master", + :default => "nil" + +attribute "openldap/slapd_rid", + :display_name => "OpenLDAP Slapd Replication ID", + :description => "Slave's ID, must be unique", + :default => "102" + +attribute "openldap/auth_type", + :display_name => "OpenLDAP Auth Type", + :description => "Used in Apache configs, AuthBasicProvider", + :default => "openldap" + +attribute "openldap/auth_binddn", + :display_name => "OpenLDAP Auth BindDN", + :description => "Used in auth_url and Apache configs, AuthBindDN", + :default => "ou=people,openldap_basedn" + +attribute "openldap/auth_bindpw", + :display_name => "OpenLDAP Auth Bind Password", + :description => "Used in Apache configs, AuthBindPassword", + :default => "nil" + +attribute "openldap/auth_url", + :display_name => "OpenLDAP Auth URL", + :description => "Used in Apache configs, AuthLDAPURL", + :default => "ldap://openldap_server/openldap_auth_binddn?uid?sub?(objectClass=*)" + diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/auth.rb b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/auth.rb new file mode 100644 index 00000000000..e46c785ea14 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/auth.rb @@ -0,0 +1,70 @@ +# +# Cookbook Name:: openldap +# Recipe:: auth +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "openldap::client" +include_recipe "openssh" +include_recipe "nscd" + +package "libnss-ldap" do + action :upgrade +end + +package "libpam-ldap" do + action :upgrade +end + +template "/etc/ldap.conf" do + source "ldap.conf.erb" + mode 0644 + owner "root" + group "root" +end + +template "/etc/ldap/ldap.conf" do + source "ldap-ldap.conf.erb" + mode 0644 + owner "root" + group "root" +end + +cookbook_file "/etc/nsswitch.conf" do + source "nsswitch.conf" + mode 0644 + owner "root" + group "root" + notifies :restart, resources(:service => "nscd"), :immediately + notifies :run, resources(:execute => [ "nscd-clear-passwd", "nscd-clear-group" ]), :immediately +end + +%w{ account auth password session }.each do |pam| + cookbook_file "/etc/pam.d/common-#{pam}" do + source "common-#{pam}" + mode 0644 + owner "root" + group "root" + notifies :restart, resources(:service => "ssh"), :delayed + end +end + +template "/etc/security/login_access.conf" do + source "login_access.conf.erb" + mode 0644 + owner "root" + group "root" +end diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/client.rb b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/client.rb new file mode 100644 index 00000000000..93f009dc9a6 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/client.rb @@ -0,0 +1,28 @@ +# +# Cookbook Name:: openldap +# Recipe:: client +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "ldap-utils" do + action :upgrade +end + +directory node[:openldap][:ssl_dir] do + mode 0755 + owner "root" + group "root" +end diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/default.rb new file mode 100644 index 00000000000..5d425c9a22a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/default.rb @@ -0,0 +1,18 @@ +# +# Cookbook Name:: openldap +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/server.rb b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/server.rb new file mode 100644 index 00000000000..3340ba0c87c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/recipes/server.rb @@ -0,0 +1,111 @@ +# +# Cookbook Name:: openldap +# Recipe:: server +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "openldap::client" + +case node[:platform] +when "ubuntu" + if (node[:platform_version].to_f >= 10.04) + package "db4.8-util" do + action :upgrade + end + else + package "db4.2-util" do + action :upgrade + end + end + cookbook_file "/var/cache/local/preseeding/slapd.seed" do + source "slapd.seed" + mode 0600 + owner "root" + group "root" + end + package "slapd" do + response_file "slapd.seed" + action :upgrade + end +else + package "db4.2-util" do + action :upgrade + end + package "slapd" do + action :upgrade + end +end + +cookbook_file "#{node[:openldap][:ssl_dir]}/#{node[:openldap][:server]}.pem" do + source "ssl/#{node[:openldap][:server]}.pem" + mode 0644 + owner "root" + group "root" +end + +service "slapd" do + action [:enable, :start] +end + +if (node[:platform] == "ubuntu") and (node[:platform_version].to_f >= 8.10) + template "/etc/default/slapd" do + source "default_slapd.erb" + owner "root" + group "root" + mode 0644 + end + + directory "#{node[:openldap][:dir]}/slapd.d" do + recursive true + owner "openldap" + group "openldap" + action :create + end + + execute "slapd-config-convert" do + command "slaptest -f #{node[:openldap][:dir]}/slapd.conf -F #{node[:openldap][:dir]}/slapd.d/" + user "openldap" + action :nothing + notifies :start, resources(:service => "slapd"), :immediately + end + + template "#{node[:openldap][:dir]}/slapd.conf" do + source "slapd.conf.erb" + mode 0640 + owner "openldap" + group "openldap" + notifies :stop, resources(:service => "slapd"), :immediately + notifies :run, resources(:execute => "slapd-config-convert") + end +else + case node[:platform] + when "debian","ubuntu" + template "/etc/default/slapd" do + source "default_slapd.erb" + owner "root" + group "root" + mode 0644 + end + end + + template "#{node[:openldap][:dir]}/slapd.conf" do + source "slapd.conf.erb" + mode 0640 + owner "openldap" + group "openldap" + notifies :restart, resources(:service => "slapd") + end +end + diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/default_slapd.erb b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/default_slapd.erb new file mode 100644 index 00000000000..48269fa0901 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/default_slapd.erb @@ -0,0 +1,47 @@ +# Location of the slapd configuration to use. If using the cn=config +# backend to store configuration in LDIF, set this variable to the +# directory containing the cn=config data; otherwise set it to the location +# of your slapd.conf file. If empty, use the compiled-in default +# (/etc/ldap/slapd.d). +SLAPD_CONF=/etc/ldap/slapd.conf + +# System account to run the slapd server under. If empty the server +# will run as root. +SLAPD_USER="openldap" + +# System group to run the slapd server under. If empty the server will +# run in the primary group of its user. +SLAPD_GROUP="openldap" + +# Path to the pid file of the slapd server. If not set the init.d script +# will try to figure it out from $SLAPD_CONF (/etc/ldap/slapd.d by +# default) +SLAPD_PIDFILE= + +# slapd normally serves ldap only on all TCP-ports 389. slapd can also +# service requests on TCP-port 636 (ldaps) and requests via unix +# sockets. +# Example usage: +# SLAPD_SERVICES="ldap://127.0.0.1:389/ ldaps:/// ldapi:///" +SLAPD_SERVICES="ldap:/// ldapi:///" + +# If SLAPD_NO_START is set, the init script will not start or restart +# slapd (but stop will still work). Uncomment this if you are +# starting slapd via some other means or if you don't want slapd normally +# started at boot. +#SLAPD_NO_START=1 + +# If SLAPD_SENTINEL_FILE is set to path to a file and that file exists, +# the init script will not start or restart slapd (but stop will still +# work). Use this for temporarily disabling startup of slapd (when doing +# maintenance, for example, or through a configuration management system) +# when you don't want to edit a configuration file. +SLAPD_SENTINEL_FILE=/etc/ldap/noslapd + +# For Kerberos authentication (via SASL), slapd by default uses the system +# keytab file (/etc/krb5.keytab). To use a different keytab file, +# uncomment this line and change the path. +#export KRB5_KTNAME=/etc/krb5.keytab + +# Additional options to pass to slapd +SLAPD_OPTIONS="" diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/ldap-ldap.conf.erb b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/ldap-ldap.conf.erb new file mode 100644 index 00000000000..479f1ae0e45 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/ldap-ldap.conf.erb @@ -0,0 +1,16 @@ +# +# LDAP Defaults +# +# Generated by Chef for <% node[:hostname] %> +# + +# See ldap.conf(5) for details +# This file should be world readable but not world writable. + +BASE <%= node[:openldap][:basedn] %> +TLS_REQCERT never +#URI ldap://ldap.example.com ldap://ldap-master.example.com:666 + +#SIZELIMIT 12 +#TIMELIMIT 15 +#DEREF never \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/ldap.conf.erb b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/ldap.conf.erb new file mode 100644 index 00000000000..f5b7db6cf03 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/ldap.conf.erb @@ -0,0 +1,26 @@ +# +# <%= node[:openldap][:dir] %>.conf generated for <%= node[:hostname] %> +# +# Managed by Chef +# + +host <%= node[:openldap][:server] %> +port 389 +bind_policy soft + +ldap_version 3 + +# Where to find data +base <%= node[:openldap][:basedn] %> +scope sub +nss_base_passwd ou=people,<%= node[:openldap][:basedn] %> +nss_base_shadow ou=people,<%= node[:openldap][:basedn] %> +nss_base_group ou=group,<%= node[:openldap][:basedn] %> +nss_base_automount ou=automount,<%= node[:openldap][:basedn] %> + +# TLS Options +ssl start_tls +tls_checkpeer no + +# Password options +pam_password md5 diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/libnss-ldap.conf.erb b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/libnss-ldap.conf.erb new file mode 100644 index 00000000000..d7d4f328089 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/libnss-ldap.conf.erb @@ -0,0 +1,23 @@ +# +# libnss-ldap.conf generated for <%= node[:hostname] %> +# +# Managed by Chef +# +# $Id:$ + +host <%= node[:openldap][:server] %> +port 389 +#bind_policy soft +nss_reconnect_tries 2 +ldap_version 3 + +# Where to find data +base <%= node[:openldap][:basedn] %> +scope sub +nss_base_passwd ou=people,<%= node[:openldap][:basedn] %> +nss_base_shadow ou=people,<%= node[:openldap][:basedn] %> +nss_base_group ou=group,<%= node[:openldap][:basedn] %> + +# TLS Options +ssl start_tls +tls_checkpeer no diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/login_access.conf.erb b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/login_access.conf.erb new file mode 100644 index 00000000000..e227f5bc2f1 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/login_access.conf.erb @@ -0,0 +1,16 @@ +# +# /etc/security/login_access.conf +# +# Prepared for <%= node[:fqdn] %> by Chef +# +<% logingroup = node[:hostname] -%> +<% logingroup = node[:hostname].sub(/^(.+?)\d+(.+)$/, '\1-\2-login') -%> +<% rootgroup = node[:hostname].sub(/^(.+?)\d+(.+)$/, '\1-\2-root') -%> + ++:root:ALL ++:admin:ALL ++:<%= logingroup %>:ALL ++:<%= rootgroup %>:ALL + +# Everyone else cannot login +-:ALL:ALL diff --git a/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/slapd.conf.erb b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/slapd.conf.erb new file mode 100644 index 00000000000..7dfa590d175 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openldap/templates/default/slapd.conf.erb @@ -0,0 +1,126 @@ +##### +# +# This is a slapd.conf file. See slapd.conf(5) for more info. +# +# Generated by Chef for <%= node[:fqdn] %> +# +# $Id:$ +#### + +# TLS configuration +TLSCertificateFile <%= node[:openldap][:dir] %>/ssl/<%= node[:openldap][:server] %>.pem +TLSCertificateKeyFile <%= node[:openldap][:dir] %>/ssl/<%= node[:openldap][:server] %>.pem + +# Schema and objectClass definitions +include <%= node[:openldap][:dir] %>/schema/core.schema +include <%= node[:openldap][:dir] %>/schema/cosine.schema +include <%= node[:openldap][:dir] %>/schema/nis.schema +include <%= node[:openldap][:dir] %>/schema/inetorgperson.schema + +# Where the pid file is put. The init.d script +# will not stop the server if you change this. +pidfile <%= node[:openldap][:run_dir] %>/slapd.pid + +# List of arguments that were passed to the server +argsfile <%= node[:openldap][:run_dir] %>/slapd.args + +# Read slapd.conf(5) for possible values +loglevel 0 + +<% unless node[:platform] == "centos" -%> +# Where the dynamically loaded modules are stored +modulepath <%= node[:openldap][:module_dir] %> +moduleload back_hdb +<% if node[:openldap][:slapd_type] == "master" -%> +moduleload syncprov +<% end -%> +<% end -%> + +# The maximum number of entries that is returned for a search operation +sizelimit 500 + +# The tool-threads parameter sets the actual amount of cpu's that is used +# for indexing. +tool-threads 1 + +####################################################################### +# Specific Backend Directives for hdb: +# Backend specific directives apply to this backend until another +# 'backend' directive occurs +backend hdb + +##### +# Database +##### +database hdb +suffix "<%= node[:openldap][:basedn] %>" +rootdn "cn=admin,<%= node[:openldap][:basedn] %>" +rootpw <%= node[:openldap][:rootpw] %> +directory "/var/lib/ldap" +lastmod on + +dbconfig set_cachesize 0 31457280 0 + +# Number of objects that can be locked at the same time. +dbconfig set_lk_max_objects 1500 +# Number of locks (both requested and granted) +dbconfig set_lk_max_locks 1500 +# Number of lockers +dbconfig set_lk_max_lockers 1500 + +## +# Indexes +## +index default pres,eq,approx,sub +index objectClass eq +index cn,ou,sn,uid,l,mail,gecos,memberUid,description +index loginShell,homeDirectory pres,eq,approx +index uidNumber,gidNumber pres,eq + +<% if node[:openldap][:slapd_type] == "master" -%> +overlay syncprov +syncprov-checkpoint 100 10 +syncprov-sessionlog 100 +<% else -%> +syncrepl rid=<%= node[:openldap][:slapd_rid] %> + provider=ldap://<%= node[:openldap][:slapd_master] %>:389 + type=refreshAndPersist + interval=01:00:00:00 + searchbase="<%= node[:openldap][:basedn] %>" + filter="(objectClass=*)" + scope=sub + schemachecking=off + bindmethod=simple + binddn="cn=syncrole,<%= node[:openldap][:basedn] %>" + starttls=yes + credentials="<%= node[:openldap][:slapd_replpw] %>" +<% end -%> +# The userPassword by default can be changed +# by the entry owning it if they are authenticated. +# Others should not be able to see it, except the +# admin entry below +# These access lines apply to database #1 only +access to attrs=userPassword,shadowLastChange + by group.exact="cn=administrators,<%= node[:openldap][:basedn] %>" write + by dn="cn=syncrole,<%= node[:openldap][:basedn] %>" read + by anonymous auth + by self write + by * none + +# Ensure read access to the base for things like +# supportedSASLMechanisms. Without this you may +# have problems with SASL not knowing what +# mechanisms are available and the like. +# Note that this is covered by the 'access to *' +# ACL below too but if you change that as people +# are wont to do you'll still need this if you +# want SASL (and possible other things) to work +# happily. +access to dn.base="" by * read + +# The admin dn has full write access, everyone else +# can read everything. +access to * + by group.exact="cn=administrators,<%= node[:openldap][:basedn] %>" write + by dn="cn=syncrole,<%= node[:openldap][:basedn] %>" read + by * read diff --git a/crowbar/change-image/dell/chef/cookbooks/openssh/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/openssh/metadata.rb new file mode 100644 index 00000000000..ddd17ef0f4c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssh/metadata.rb @@ -0,0 +1,11 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs openssh" +version "0.8.1" + +recipe "openssh", "Installs openssh" + +%w{ redhat centos fedora ubuntu debian arch scientific }.each do |os| + supports os +end diff --git a/crowbar/change-image/dell/chef/cookbooks/openssh/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/openssh/recipes/default.rb new file mode 100644 index 00000000000..0bbc00d3252 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssh/recipes/default.rb @@ -0,0 +1,55 @@ +# +# Cookbook Name:: openssh +# Recipe:: default +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +packages = case node[:platform] + when "centos","redhat","fedora","scientific" + %w{openssh-clients openssh} + when "arch" + %w{openssh} + else + %w{openssh-client openssh-server} + end + +packages.each do |pkg| + package pkg +end + +service "ssh" do + case node[:platform] + when "centos","redhat","fedora","arch","scientific" + service_name "sshd" + else + service_name "ssh" + end + supports value_for_platform( + "debian" => { "default" => [ :restart, :reload, :status ] }, + "ubuntu" => { + "8.04" => [ :restart, :reload ], + "default" => [ :restart, :reload, :status ] + }, + "centos" => { "default" => [ :restart, :reload, :status ] }, + "redhat" => { "default" => [ :restart, :reload, :status ] }, + "fedora" => { "default" => [ :restart, :reload, :status ] }, + "scientific" => { "default" => [ :restart, :reload, :status ] }, + "arch" => { "default" => [ :restart ] }, + "default" => { "default" => [:restart, :reload ] } + ) + action [ :enable, :start ] +end + diff --git a/crowbar/change-image/dell/chef/cookbooks/openssh/templates/default/port_ssh.erb b/crowbar/change-image/dell/chef/cookbooks/openssh/templates/default/port_ssh.erb new file mode 100644 index 00000000000..9265619e27e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssh/templates/default/port_ssh.erb @@ -0,0 +1,2 @@ +# SSH +-A FWR -p tcp -m tcp --dport 22 -j ACCEPT \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/openssl/README.rdoc b/crowbar/change-image/dell/chef/cookbooks/openssl/README.rdoc new file mode 100644 index 00000000000..47d6727f38b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssl/README.rdoc @@ -0,0 +1,33 @@ += DESCRIPTION: + +Library provides a method to generate secure passwords for use in recipes. + += REQUIREMENTS: + +OpenSSL Ruby bindings must be installed, which are a requirement for Chef anyway. + += USAGE: + +Most often this will be used to generate a secure password for an attribute. + + include Opscode::OpenSSL::Password + + set_unless[:my_password] = secure_password + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () + +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/openssl/libraries/secure_password.rb b/crowbar/change-image/dell/chef/cookbooks/openssl/libraries/secure_password.rb new file mode 100644 index 00000000000..e5fd638baa9 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssl/libraries/secure_password.rb @@ -0,0 +1,37 @@ +# +# Cookbook Name:: openssl +# Library:: secure_password +# Author:: Joshua Timberman +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'openssl' + +module Opscode + module OpenSSL + module Password + def secure_password + pw = String.new + + while pw.length < 20 + pw << ::OpenSSL::Random.random_bytes(1).gsub(/\W/, '') + end + + pw + end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/openssl/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/openssl/metadata.rb new file mode 100644 index 00000000000..7f8e79b6484 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssl/metadata.rb @@ -0,0 +1,8 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs/Configures openssl" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1" + +recipe "openssl", "Empty, this cookbook provides a library, see README" diff --git a/crowbar/change-image/dell/chef/cookbooks/openssl/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/openssl/recipes/default.rb new file mode 100644 index 00000000000..9850a285679 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/openssl/recipes/default.rb @@ -0,0 +1,19 @@ +# +# Cookbook Name:: openssl +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + diff --git a/crowbar/change-image/dell/chef/cookbooks/packages/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/packages/attributes/default.rb new file mode 100644 index 00000000000..94158afef54 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/packages/attributes/default.rb @@ -0,0 +1,3 @@ +# Toggle for recipes to determine if we should rely on distribution packages +# or gems. +default[:packages][:dist_only] = false diff --git a/crowbar/change-image/dell/chef/cookbooks/packages/libraries/packages.rb b/crowbar/change-image/dell/chef/cookbooks/packages/libraries/packages.rb new file mode 100644 index 00000000000..2d2e0e518ee --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/packages/libraries/packages.rb @@ -0,0 +1,9 @@ +# Yay for helpers! + +class Chef + class Recipe + def dist_only? + node[:packages][:dist_only] + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/packages/metadata.json b/crowbar/change-image/dell/chef/cookbooks/packages/metadata.json new file mode 100644 index 00000000000..7572e0ee1fd --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/packages/metadata.json @@ -0,0 +1,70 @@ +{ + "platforms": { + "centos": [ + + ], + "redhat": [ + + ] + }, + "suggestions": { + + }, + "license": "Apache 2.0", + "conflicting": { + + }, + "long_description": "", + "providing": { + + }, + "description": "Helper library to determine whether distribution-only packages are installed", + "version": "0.9.2", + "maintainer": "Opscode, Inc.", + "replacing": { + + }, + "attributes": { + "packages": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "type": "hash", + "recipes": [ + + ], + "display_name": "Packages", + "description": "Hash of Packages attributes" + }, + "packages/dist_only": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "false", + "type": "string", + "recipes": [ + + ], + "display_name": "Packages Distribution Only?", + "description": "Set to only use distribution-provided packages" + } + }, + "maintainer_email": "cookbooks@opscode.com", + "name": "packages", + "recipes": { + "packages": "Empty, this cookbook provides a library for helping determine whther distribution only packages should be installed" + }, + "groupings": { + + }, + "dependencies": { + + }, + "recommendations": { + + } +} \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/packages/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/packages/metadata.rb new file mode 100644 index 00000000000..1c0fe691cd5 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/packages/metadata.rb @@ -0,0 +1,22 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Helper library to determine whether distribution-only packages are installed" +version "0.9.2" + +recipe "packages", "Empty, this cookbook provides a library for helping determine whther distribution only packages should be installed" + +%w{redhat centos}.each do |os| + supports os +end + +attribute "packages", + :display_name => "Packages", + :description => "Hash of Packages attributes", + :type => "hash" + +attribute "packages/dist_only", + :display_name => "Packages Distribution Only?", + :description => "Set to only use distribution-provided packages", + :default => "false" + diff --git a/crowbar/change-image/dell/chef/cookbooks/packages/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/packages/recipes/default.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/README.md b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/README.md new file mode 100644 index 00000000000..39e9cc07c50 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/README.md @@ -0,0 +1,76 @@ +Description +==== + +Installs passenger for Apache 2. + +Changes +==== + +## v0.99.0: + +* Upgrade to passenger 3.0.7 +* Attributes are all "default" +* Install curl development headers +* Move PassengerMaxPoolSize to config of module instead of vhost. + +Requirements +==== + +## Platform + +Tested on Ubuntu 10.04. Should work on any Ubuntu/Debian platforms. + +## Cookbooks + +Opscode cookbooks: + +* apache2 +* build-essential + +Attributes +==== + +* `passenger[:version]` - Specify the version of passenger to install. +* `passenger[:max_pool_size]` - Sets PassengerMaxPoolSize in the Apache module config. +* `passenger[:root_path]` - The location of the passenger gem. +* `passenger[:module_path]` - The location of the compiled passenger apache module. + +Usage +==== + +For example, to run a Rails application on passenger: + + include_recipe "rails" + include_recipe "passenger" + + web_app "myproj" do + docroot "/srv/myproj/public" + server_name "myproj.#{node[:domain]}" + server_aliases [ "myproj", node[:hostname] ] + rails_env "production" + end + +A sample config template is provided, `web_app.conf.erb`. If this is suitable for your application, add 'cookbook "passenger"' to the define above to use that template. Otherwise, copy the template to the cookbook where you're using `web_app`, and modify as needed. The cookbook parameter is optional, if omitted it will search the cookbook where the define is used. + +License and Author +==== + +Author:: Joshua Timberman () +Author:: Joshua Sierles () +Author:: Michael Hale () + +Copyright:: 2009-2011, Opscode, Inc +Copyright:: 2009, 37signals +Coprighty:: 2009, Michael Hale + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/attributes/default.rb new file mode 100644 index 00000000000..9780df88a17 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/attributes/default.rb @@ -0,0 +1,4 @@ +default[:passenger][:version] = "3.0.7" +default[:passenger][:max_pool_size] = "6" +default[:passenger][:root_path] = "#{languages[:ruby][:gems_dir]}/gems/passenger-#{passenger[:version]}" +default[:passenger][:module_path] = "#{passenger[:root_path]}/ext/apache2/mod_passenger.so" diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/metadata.rb new file mode 100644 index 00000000000..e9913a38794 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/metadata.rb @@ -0,0 +1,16 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs passenger for Apache2" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version "0.99.0" + +recipe "passenger_apache2", "Installs Passenger as an Apache module" +recipe "passenger_apache2::mod_rails", "Enables Apache module configuration for passenger module" + +depends "apache2" +depends "build-essential" + +%w{ redhat centos ubuntu debian }.each do |os| + supports os +end diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/recipes/default.rb new file mode 100644 index 00000000000..819bcc3a14f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/recipes/default.rb @@ -0,0 +1,49 @@ +# +# Cookbook Name:: passenger_apache2 +# Recipe:: default +# +# Author:: Joshua Timberman () +# Author:: Joshua Sierles () +# Author:: Michael Hale () +# +# Copyright:: 2009, Opscode, Inc +# Copyright:: 2009, 37signals +# Coprighty:: 2009, Michael Hale +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "apache2" +include_recipe "build-essential" + +if platform?("centos","redhat") + package "httpd-devel" + package "curl-devel" +elsif platform?("ubuntu","debian") + package "libapache2-mod-passenger" + return +else + %w{ apache2-prefork-dev libapr1-dev libcurl4-gnutls-dev }.each do |pkg| + package pkg do + action :upgrade + end + end +end + +gem_package "passenger" do + version node[:passenger][:version] +end + +execute "passenger_module" do + command 'echo -en "\n\n\n\n" | passenger-install-apache2-module' + creates node[:passenger][:module_path] +end diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/recipes/mod_rails.rb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/recipes/mod_rails.rb new file mode 100644 index 00000000000..c1a22c7413f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/recipes/mod_rails.rb @@ -0,0 +1,43 @@ +# +# Cookbook Name:: passenger_apache2 +# Recipe:: default +# +# Author:: Joshua Timberman () +# Author:: Joshua Sierles () +# Author:: Michael Hale () +# +# Copyright:: 2009, Opscode, Inc +# Copyright:: 2009, 37signals +# Coprighty:: 2009, Michael Hale +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_recipe "passenger_apache2" + +template "#{node[:apache][:dir]}/mods-available/passenger.load" do + cookbook "passenger_apache2" + source "passenger.load.erb" + owner "root" + group "root" + mode 0755 +end + +template "#{node[:apache][:dir]}/mods-available/passenger.conf" do + cookbook "passenger_apache2" + source "passenger.conf.erb" + owner "root" + group "root" + mode 0755 +end + +apache_module "passenger" diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger.conf.erb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger.conf.erb new file mode 100644 index 00000000000..65c260a5100 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger.conf.erb @@ -0,0 +1,3 @@ +PassengerRoot <%= node[:passenger][:root_path] %> +PassengerRuby <%= node[:languages][:ruby][:ruby_bin] %> +PassengerMaxPoolSize <%= node[:passenger][:max_pool_size] %> diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger.load.erb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger.load.erb new file mode 100644 index 00000000000..8b6e3ab4507 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger.load.erb @@ -0,0 +1 @@ +LoadModule passenger_module <%= node[:passenger][:module_path] %> diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger_web_app.conf.erb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger_web_app.conf.erb new file mode 100644 index 00000000000..473fee0749a --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/passenger_web_app.conf.erb @@ -0,0 +1,32 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsEnv <%= @params[:rails_env] %> + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + LogLevel info + ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + + RewriteEngine On + RewriteLog <%= node[:apache][:log_dir] %>/<%= @application_name %>-rewrite.log + RewriteLogLevel 0 + # Canonical host + RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC] + RewriteCond %{HTTP_HOST} !^$ + RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301] + + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + + diff --git a/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/web_app.conf.erb b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/web_app.conf.erb new file mode 100644 index 00000000000..7aca9168df7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/passenger_apache2/templates/default/web_app.conf.erb @@ -0,0 +1,33 @@ + + ServerName <%= @params[:server_name] %> + ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsEnv <%= @params[:rails_env] %> + RailsAllowModRewrite on + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + + LogLevel info + ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log + CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined + + RewriteEngine On + RewriteLog <%= node[:apache][:log_dir] %>/<%= @application_name %>-rewrite.log + RewriteLogLevel 0 + # Canonical host + RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC] + RewriteCond %{HTTP_HOST} !^$ + RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301] + + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^.*$ /system/maintenance.html [L] + + diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/README.md b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/README.md new file mode 100644 index 00000000000..9d47d5a0074 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/README.md @@ -0,0 +1,86 @@ +Description +=========== +This is a cookbook for managing RabbitMQ with Chef. It uses the default settings, but can also be configured via attributes. + +Recipes +======= +default +------- +Installs `rabbitmq-server` from RabbitMQ.com's APT repository. The distribution-provided version was quite old and newer features were needed. + +cluster +------- +Configures nodes to be members of a RabbitMQ cluster, but does not actually join them. + +Resources/Providers +=================== +There are 2 LWRPs for interacting with RabbitMQ. + +user +---- +Adds and deletes users, fairly simplistic permissions management. + +- `:add` adds a `user` with a `password` +- `:delete` deletes a `user` +- `:set_permissions` sets the `permissions` for a `user`, `vhost` is optional +- `:clear_permissions` clears the permissions for a `user` + +### Examples +``` ruby +rabbitmq_user "guest" do + action :delete +end + +rabbitmq_user "nova" do + password "sekret" + action :add +end + +rabbitmq_user "nova" do + vhost "/nova" + permissions "\".*\" \".*\" \".*\"" + action :set_permissions +end +``` + +vhost +----- +Adds and deletes vhosts. + +- `:add` adds a `vhost` +- `:delete` deletes a `vhost` + +### Example +``` ruby +rabbitmq_vhost "/nova" do + action :add +end +``` + +Limitations +=========== +It is quite useful as is, but clustering configuration does not currently do the dance to join the cluster members to each other. + +The rabbitmq::chef recipe was only used for the chef-server cookbook and has been moved to chef-server::rabbitmq. + +License and Author +================== +Author:: Benjamin Black + +Author:: Daniel DeLeo + +Author:: Matt Ray + +Copyright:: 2009-2011 Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/attributes/default.rb new file mode 100644 index 00000000000..50016eb76c4 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/attributes/default.rb @@ -0,0 +1,10 @@ +default[:rabbitmq][:nodename] = node[:hostname] +default[:rabbitmq][:address] = nil +default[:rabbitmq][:port] = nil +default[:rabbitmq][:config] = nil +default[:rabbitmq][:logdir] = nil +default[:rabbitmq][:mnesiadir] = nil +#clustering +default[:rabbitmq][:cluster] = "no" +default[:rabbitmq][:cluster_config] = "/etc/rabbitmq/rabbitmq_cluster.config" +default[:rabbitmq][:cluster_disk_nodes] = [] diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/amqp_client-2.3.1.ez b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/amqp_client-2.3.1.ez new file mode 100644 index 00000000000..6ef6d4ff148 Binary files /dev/null and b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/amqp_client-2.3.1.ez differ diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/mochiweb-2.3.1.ez b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/mochiweb-2.3.1.ez new file mode 100644 index 00000000000..05d5f910d66 Binary files /dev/null and b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/mochiweb-2.3.1.ez differ diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-management-2.3.1.ez b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-management-2.3.1.ez new file mode 100644 index 00000000000..1cd4b6ea88c Binary files /dev/null and b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-management-2.3.1.ez differ diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-management-agent-2.3.1.ez b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-management-agent-2.3.1.ez new file mode 100644 index 00000000000..6638fdbda08 Binary files /dev/null and b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-management-agent-2.3.1.ez differ diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-mochiweb-2.3.1.ez b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-mochiweb-2.3.1.ez new file mode 100644 index 00000000000..5f7cc046c19 Binary files /dev/null and b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/rabbitmq-mochiweb-2.3.1.ez differ diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/webmachine-2.3.1.ez b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/webmachine-2.3.1.ez new file mode 100644 index 00000000000..012666a0275 Binary files /dev/null and b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/files/default/plugins/webmachine-2.3.1.ez differ diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/metadata.rb new file mode 100644 index 00000000000..612eb3508b8 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/metadata.rb @@ -0,0 +1,63 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs and configures RabbitMQ server" +version "1.2.0" +recipe "rabbitmq", "Install and configure RabbitMQ" +recipe "rabbitmq::cluster", "Set up RabbitMQ clustering." +depends "apt" + +%w{ubuntu debian}.each do |os| + supports os +end + +attribute "rabbitmq", + :display_name => "RabbitMQ", + :description => "Hash of RabbitMQ attributes", + :type => "hash" + +attribute "rabbitmq/nodename", + :display_name => "RabbitMQ Erlang node name", + :description => "The Erlang node name for this server.", + :default => "node[:hostname]" + +attribute "rabbitmq/address", + :display_name => "RabbitMQ server IP address", + :description => "IP address to bind." + +attribute "rabbitmq/port", + :display_name => "RabbitMQ server port", + :description => "TCP port to bind." + +attribute "rabbitmq/config", + :display_name => "RabbitMQ config file to load", + :description => "Path to the rabbitmq.config file, if any." + +attribute "rabbitmq/logdir", + :display_name => "RabbitMQ log directory", + :description => "Path to the directory for log files." + +attribute "rabbitmq/mnesiadir", + :display_name => "RabbitMQ Mnesia database directory", + :description => "Path to the directory for Mnesia database files." + +attribute "rabbitmq/cluster", + :display_name => "RabbitMQ clustering", + :description => "Whether to activate clustering.", + :default => "no" + +attribute "rabbitmq/cluster_config", + :display_name => "RabbitMQ clustering configuration file", + :description => "Path to the clustering configuration file, if cluster is yes.", + :default => "/etc/rabbitmq/rabbitmq_cluster.config" + +attribute "rabbitmq/cluster_disk_nodes", + :display_name => "RabbitMQ cluster disk nodes", + :description => "Array of member Erlang nodenames for the disk-based storage nodes in the cluster.", + :default => [], + :type => "array" + +attribute "rabbitmq/erlang_cookie", + :display_name => "RabbitMQ Erlang cookie", + :description => "Access cookie for clustering nodes. There is no default." + diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/providers/user.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/providers/user.rb new file mode 100644 index 00000000000..bb1b3ca50ea --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/providers/user.rb @@ -0,0 +1,66 @@ +# +# Cookbook Name:: rabbitmq +# Provider:: user +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :add do + execute "rabbitmqctl add_user #{new_resource.user} #{new_resource.password}" do + not_if "rabbitmqctl list_users | grep #{new_resource.user}" + Chef::Log.info "Adding RabbitMQ user '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end +end + +action :delete do + execute "rabbitmqctl delete_user #{new_resource.user}" do + only_if "rabbitmqctl list_users | grep #{new_resource.user}" + Chef::Log.info "Deleting RabbitMQ user '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end +end + +action :set_permissions do + if new_resource.vhost + execute "rabbitmqctl set_permissions -p #{new_resource.vhost} #{new_resource.user} #{new_resource.permissions}" do + not_if "rabbitmqctl list_user_permissions | grep #{new_resource.user}" + Chef::Log.info "Setting RabbitMQ user permissions for '#{new_resource.user}' on vhost #{new_resource.vhost}." + new_resource.updated_by_last_action(true) + end + else + execute "rabbitmqctl set_permissions #{new_resource.user} #{new_resource.permissions}" do + not_if "rabbitmqctl list_user_permissions | grep #{new_resource.user}" + Chef::Log.info "Setting RabbitMQ user permissions for '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end + end +end + +action :clear_permissions do + if new_resource.vhost + execute "rabbitmqctl clear_permissions -p #{new_resource.vhost} #{new_resource.user}" do + only_if "rabbitmqctl list_user_permissions | grep #{new_resource.user}" + Chef::Log.info "Clearing RabbitMQ user permissions for '#{new_resource.user}' from vhost #{new_resource.vhost}." + new_resource.updated_by_last_action(true) + end + else + execute "rabbitmqctl clear_permissions #{new_resource.user}" do + only_if "rabbitmqctl list_user_permissions | grep #{new_resource.user}" + Chef::Log.info "Clearing RabbitMQ user permissions for '#{new_resource.user}'." + new_resource.updated_by_last_action(true) + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/providers/vhost.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/providers/vhost.rb new file mode 100644 index 00000000000..96f069a658b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/providers/vhost.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: rabbitmq +# Provider:: vhost +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +action :add do + execute "rabbitmqctl add_vhost #{new_resource.vhost}" do + not_if "rabbitmqctl list_vhosts | grep #{new_resource.vhost}" + Chef::Log.info "Adding RabbitMQ vhost '#{new_resource.vhost}'." + new_resource.updated_by_last_action(true) + end +end + +action :delete do + execute "rabbitmqctl delete_vhost #{new_resource.vhost}" do + only_if "rabbitmqctl list_vhosts | grep #{new_resource.vhost}" + Chef::Log.info "Deleting RabbitMQ vhost '#{new_resource.vhost}'." + new_resource.updated_by_last_action(true) + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/recipes/cluster.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/recipes/cluster.rb new file mode 100644 index 00000000000..9191f6ae62e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/recipes/cluster.rb @@ -0,0 +1,35 @@ +# +# Cookbook Name:: rabbitmq +# Recipe:: cluster +# +# Copyright 2009, Benjamin Black +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +include_recipe "rabbitmq::default" + +template "/var/lib/rabbitmq/.erlang.cookie" do + source "doterlang.cookie.erb" + owner "rabbitmq" + group "rabbitmq" + mode 0400 +end + +template "/etc/rabbitmq/rabbitmq_cluster.config" do + source "rabbitmq_cluster.config.erb" + owner "root" + group "root" + mode 0644 + notifies :restart, resources(:service => "rabbitmq-server") +end + diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/recipes/default.rb new file mode 100644 index 00000000000..b52c8ef4895 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/recipes/default.rb @@ -0,0 +1,59 @@ +# +# Cookbook Name:: rabbitmq +# Recipe:: default +# +# Copyright 2009, Benjamin Black +# Copyright 2009-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +## use the RabbitMQ repository instead of Ubuntu or Debian's +## because there are very useful features in the newer versions +#apt_repository "rabbitmq" do +# uri "http://www.rabbitmq.com/debian/" +# distribution "testing" +# components ["main"] +# key "http://www.rabbitmq.com/rabbitmq-signing-key-public.asc" +# action :add +#end + +# rabbitmq-server is not well-behaved as far as managed services goes +# we'll need to add a LWRP for calling rabbitmqctl stop +# while still using /etc/init.d/rabbitmq-server start +# because of this we just put the rabbitmq-env.conf in place and let it rip + +remote_directory "/usr/lib/rabbitmq/lib/rabbitmq_server-2.3.1/plugins" do + source "plugins" + owner "root" + group "root" + mode 0755 + files_mode 0644 +end + +directory "/etc/rabbitmq/" do + owner "root" + group "root" + mode 0755 + action :create +end + +template "/etc/rabbitmq/rabbitmq-env.conf" do + source "rabbitmq-env.conf.erb" + owner "root" + group "root" + mode 0644 +end + +package "rabbitmq-server" + diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/resources/user.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/resources/user.rb new file mode 100644 index 00000000000..64fda96c9b0 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/resources/user.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: rabbitmq +# Resource:: user +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :delete, :set_permissions, :clear_permissions + +attribute :user, :kind_of => String, :name_attribute => true +attribute :password, :kind_of => String +attribute :vhost, :kind_of => String +attribute :permissions, :kind_of => String diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/resources/vhost.rb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/resources/vhost.rb new file mode 100644 index 00000000000..8acbb854f7d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/resources/vhost.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: rabbitmq +# Resource:: vhost +# +# Copyright 2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +actions :add, :delete + +attribute :vhost, :kind_of => String, :name_attribute => true diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/doterlang.cookie.erb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/doterlang.cookie.erb new file mode 100644 index 00000000000..80be8470090 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/doterlang.cookie.erb @@ -0,0 +1 @@ +<%= node[:rabbitmq][:erlang_cookie] %> \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/rabbitmq-env.conf.erb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/rabbitmq-env.conf.erb new file mode 100644 index 00000000000..06bf7eb962d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/rabbitmq-env.conf.erb @@ -0,0 +1,10 @@ +### +# Generated by Chef for <%= node[:fqdn] %> +### + +NODENAME=<%= node[:rabbitmq][:nodename] %> +<% if node[:rabbitmq][:address] %>NODE_IP_ADDRESS=<%= node[:rabbitmq][:address] %><% end %> +<% if node[:rabbitmq][:port] %>NODE_PORT=<%= node[:rabbitmq][:port] %><% end %> +<% if node[:rabbitmq][:config] %>CONFIG_FILE=<%= node[:rabbitmq][:config] %><% end %> +<% if node[:rabbitmq][:logdir] %>LOG_BASE=<%= node[:rabbitmq][:logdir] %><% end %> +<% if node[:rabbitmq][:mnesiadir] %>MNESIA_BASE=<%= node[:rabbitmq][:mnesiadir] %><% end %> diff --git a/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/rabbitmq_cluster.config.erb b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/rabbitmq_cluster.config.erb new file mode 100644 index 00000000000..8d806dd07f7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rabbitmq/templates/default/rabbitmq_cluster.config.erb @@ -0,0 +1,5 @@ +%%% +%% Generated by Chef for <%= node[:fqdn] %> +%%% + +[<%= node[:rabbitmq][:cluster_disk_nodes].map{|n| "\'#{n}\'"}.join(',') %>]. diff --git a/crowbar/change-image/dell/chef/cookbooks/rails/README.rdoc b/crowbar/change-image/dell/chef/cookbooks/rails/README.rdoc new file mode 100644 index 00000000000..70e858331fe --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rails/README.rdoc @@ -0,0 +1,54 @@ += DESCRIPTION: + +Installs Rails and contains Rails tuning parameters. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 8.10, should work on other platforms. + +== Cookbooks: + +Opscode cookbooks, http://github.com/opscode/cookbooks/tree/master: + +* ruby +* apache2 +* passenger + += ATTRIBUTES: + +* rails[:version] - Install the specified version. Default false (installs latest). +* rails[:environment] - Set Rails environment. Default production. + += USAGE: + +The recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define. + + include_recipe "apache2" + include_recipe "passenger" + include_recipe "rails" + + web_app "some_rails_app" do + docroot "/srv/some_rails_app/public" + template "some_rails_app.conf.erb" + end + +We provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman () +Copyright:: 2009, Opscode, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/rails/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/rails/attributes/default.rb new file mode 100644 index 00000000000..94870533c9d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rails/attributes/default.rb @@ -0,0 +1,3 @@ +default[:rails][:version] = false +default[:rails][:environment] = "production" +default[:rails][:max_pool_size] = 4 diff --git a/crowbar/change-image/dell/chef/cookbooks/rails/metadata.json b/crowbar/change-image/dell/chef/cookbooks/rails/metadata.json new file mode 100644 index 00000000000..1f56ae8be3f --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rails/metadata.json @@ -0,0 +1,112 @@ +{ + "platforms": { + "debian": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "ubuntu": [ + + ], + "redhat": [ + + ] + }, + "suggestions": { + + }, + "license": "Apache 2.0", + "conflicting": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls Rails and contains Rails tuning parameters.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 8.10, should work on other platforms.\n\n== Cookbooks:\n\nOpscode cookbooks, http://github.com/opscode/cookbooks/tree/master:\n\n* ruby \n* apache2 \n* passenger \n\n= ATTRIBUTES: \n\n* rails[:version] - Install the specified version. Default false (installs latest).\n* rails[:environment] - Set Rails environment. Default production.\n\n= USAGE:\n\nThe recommended Rails application deployment method is Passenger and use the Apache2 cookbook's web_app define.\n\n include_recipe \"apache2\"\n include_recipe \"passenger\"\n include_recipe \"rails\"\n\n web_app \"some_rails_app\" do\n docroot \"/srv/some_rails_app/public\"\n template \"some_rails_app.conf.erb\"\n end\n\nWe provide an example rails application vhost config file in this cookbook. Remember, for Passenger, DocumentRoot (docroot) needs 'public'. Per the web_app define, other parameters can be passed arbitrarily and used in the template. \n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman ()\nCopyright:: 2009, Opscode, Inc\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "providing": { + + }, + "description": "Installs rails and provides a sample template for use with passenger", + "version": "0.9.2", + "maintainer": "Opscode, Inc.", + "replacing": { + + }, + "attributes": { + "rails/version": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "false", + "type": "string", + "recipes": [ + + ], + "display_name": "Rails Version", + "description": "Specify the version of Rails to install" + }, + "rails": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "type": "hash", + "recipes": [ + + ], + "display_name": "Rails", + "description": "Hash of Rails attributes" + }, + "rails/max_pool_size": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "4", + "type": "string", + "recipes": [ + + ], + "display_name": "Rails Max Pool Size", + "description": "Specify the MaxPoolSize in the Apache vhost" + }, + "rails/environment": { + "required": "optional", + "calculated": false, + "choice": [ + + ], + "default": "production", + "type": "string", + "recipes": [ + + ], + "display_name": "Rails Environment", + "description": "Specify the environment to use for Rails" + } + }, + "maintainer_email": "cookbooks@opscode.com", + "name": "rails", + "recipes": { + "rails": "Installs rails gems" + }, + "groupings": { + + }, + "dependencies": { + "apache2": [ + + ], + "ruby": [ + + ] + }, + "recommendations": { + + } +} \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/rails/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/rails/metadata.rb new file mode 100644 index 00000000000..856b1ee6447 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rails/metadata.rb @@ -0,0 +1,37 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs rails and provides a sample template for use with passenger" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.9.2" + +recipe "rails", "Installs rails gems" + +%w{ ruby apache2 }.each do |cb| + depends cb +end + +%w{ ubuntu debian centos redhat fedora}.each do |os| + supports os +end + +attribute "rails", + :display_name => "Rails", + :description => "Hash of Rails attributes", + :type => "hash" + +attribute "rails/version", + :display_name => "Rails Version", + :description => "Specify the version of Rails to install", + :default => "false" + +attribute "rails/environment", + :display_name => "Rails Environment", + :description => "Specify the environment to use for Rails", + :default => "production" + +attribute "rails/max_pool_size", + :display_name => "Rails Max Pool Size", + :description => "Specify the MaxPoolSize in the Apache vhost", + :default => "4" + diff --git a/crowbar/change-image/dell/chef/cookbooks/rails/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/rails/recipes/default.rb new file mode 100644 index 00000000000..628703f3043 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rails/recipes/default.rb @@ -0,0 +1,38 @@ +# +# Cookbook Name:: rails +# Recipe:: default +# +# Copyright 2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "ruby" + +case node[:platform] +when "ubuntu","debian" + package "rails" do + action :install + end +else + %w{ rails actionmailer actionpack activerecord activesupport activeresource }.each do |rails_gem| + gem_package rails_gem do + if node[:rails][:version] + version node[:rails][:version] + action :install + else + action :install + end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/rails/templates/default/rails_app.conf.erb b/crowbar/change-image/dell/chef/cookbooks/rails/templates/default/rails_app.conf.erb new file mode 100644 index 00000000000..fc5724461ef --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/rails/templates/default/rails_app.conf.erb @@ -0,0 +1,30 @@ + + ServerName <%= @params[:server_name] %> + DocumentRoot <%= @params[:docroot] %> + + RailsBaseURI / + RailsMaxPoolSize <%= node[:rails][:max_pool_size] %> + RailsPoolIdleTime 3600 + RailsEnv '<%= node[:rails][:environment] %>' + + LogLevel info + ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>_error.log + CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>_access.log combined + + ErrorDocument 404 /404.html + ErrorDocument 500 /500.html + + RewriteEngine On + + # Handle maintenance mode + RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f + RewriteCond %{SCRIPT_FILENAME} !maintenance.html + RewriteRule ^/(.*)$ /system/maintenance.html [L] + + > + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/README.rdoc b/crowbar/change-image/dell/chef/cookbooks/ruby/README.rdoc new file mode 100644 index 00000000000..48147ae873e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/README.rdoc @@ -0,0 +1,60 @@ += DESCRIPTION: + +Installs Ruby and related packages. + += REQUIREMENTS: + +== Platform: + +Tested on Ubuntu 10.04. Debian and Gentoo should also work fully. CentOS, Red Hat, Fedora and Arch are partially supported. + += ATTRIBUTES: + +* +languages[:ruby][:default_version]+ - The Ruby version to install with the ruby recipe and create symlinks for with the symlinks recipe. + += USAGE: + +Previous versions of this cookbook gave you no control over which version of Ruby would be installed. We are now in the middle of an awkward period where you are equally likely to want 1.8 or 1.9. You may even want both. This is now catered for. To install specific versions side-by-side, use the 1.8, 1.9 or 1.9.1 recipes. The ruby recipe will install the version specified by +languages[:ruby][:default_version]+. If you want to do something other than install these packages, the +ruby_packages+ definition is provided as a wrapper around the package resource. Just specify the version number. + +For example, to use the default recipe in a role named "base", use 'ruby' in the run list and set the +languages[:ruby][:default_version]+ attribute: + + { + "name": "base", + "description": "Base role is applied to all systems", + "json_class": "Chef::Role", + "default_attributes": { + }, + "override_attributes": { + "languages": { + "ruby": { + "default_version": "1.8" + } + } + }, + "chef_type": "role", + "run_list": [ + "recipe[ruby]" + ] + } + +Many scripts, including those provided by Rails, don't ask for a particular version of Ruby such as "ruby1.8" and simply look for "ruby" instead. Sometimes a symlink is provided and sometimes the executable is simply called "ruby" in the first place but generally speaking, it is difficult to predict this behaviour, especially when Ruby Gems is thrown into the mix. The symlinks recipe seeks to relieve you of this headache by creating symlinks for the common executables pointing to the Ruby version specified by +languages[:ruby][:default_version]+. This is also available as a definition called +ruby_symlinks+, which is a wrapper around the link resource. As before, just specify the version number. Non-symlinks will not be overwritten unless you set force to true. You can also set a path other than /usr/bin if necessary. + +*IMPORTANT!* Only Ubuntu, Debian and Gentoo support installing a specific Ruby version at all. yum-based distributions install 1.8 by default but require you to give the full package version otherwise. Maybe some magic could be added to Chef? Arch installs 1.9.2 by default but 1.8 is only available from AUR. Additionally, Ubuntu and Debian group 1.9.2 with 1.9.1 while Gentoo lumps all 1.9 releases together. + += LICENSE and AUTHOR: + +Author:: Joshua Timberman (), James Le Cuirot () + +Copyright:: 2009-2010, Opscode, Inc; 2010, FindsYou Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/definitions/ruby_packages.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/definitions/ruby_packages.rb new file mode 100644 index 00000000000..2171d5c4f9c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/definitions/ruby_packages.rb @@ -0,0 +1,78 @@ +# +# Cookbook Name:: ruby +# Definition:: ruby_packages +# +# Copyright 2008-2009, Opscode, Inc. +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :ruby_packages, :action => :install do + rv = params[:name].to_s + raise "A Ruby version such as 1.8, 1.9 or 1.9.1 must be given" if rv.empty? + + packages = case node[:platform] + when "ubuntu","debian" + [ + "ruby#{rv}", + "ruby#{rv}-dev", +# "rdoc#{rv}", +# "ri#{rv}", +# "irb#{rv}", +# "libopenssl-ruby#{rv}", + ("libshadow-ruby1.8" if rv == "1.8") + ].compact + + when "gentoo" + rv = rv.slice(0..2) + target = "ruby" + rv.delete('.') + + [ + # ruby-ssl is before ruby to ensure that ruby is initially + # installed with the ssl USE flag enabled. + "virtual/ruby-ssl:#{target}", + "dev-lang/ruby:#{rv}", + "virtual/ruby-rdoc:#{target}", + ("dev-ruby/ruby-shadow" if rv == "1.8") + ].compact + + when "centos","redhat","fedora" + # yum requires full version numbers. :( + %w{ + ruby + ruby-libs + ruby-devel + ruby-docs + ruby-ri + ruby-irb + ruby-rdoc + ruby-mode + } + + when "arch" + # 1.8 only available from AUR. :( + %w{ + ruby + ruby-docs + } + end + + unless packages.nil? + packages.each do |pkg| + package pkg do + action params[:action] + end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/definitions/ruby_symlinks.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/definitions/ruby_symlinks.rb new file mode 100644 index 00000000000..2e45e3a1a90 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/definitions/ruby_symlinks.rb @@ -0,0 +1,42 @@ +# +# Cookbook Name:: ruby +# Definition:: ruby_symlinks +# +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :ruby_symlinks, :action => :create, :force => false, :path => '/usr/bin' do + rv = params[:name].to_s + rv = rv.slice(0..2).delete(".") if node[:platform] == "gentoo" + + %w( ruby irb erb ri testrb rdoc gem rake ).each do |name| + path = File.join(params[:path], name) + scope = self + + link path do + to path + rv + action params[:action] + + unless params[:force] + not_if do + if File.exists?(path) and not File.symlink?(path) + scope.log "Not modifying non-symbolic-link #{path}" + true + end + end + end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/metadata.json b/crowbar/change-image/dell/chef/cookbooks/ruby/metadata.json new file mode 100644 index 00000000000..cf8eb8592a8 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/metadata.json @@ -0,0 +1,79 @@ +{ + "platforms": { + "debian": [ + + ], + "fedora": [ + + ], + "centos": [ + + ], + "arch": [ + + ], + "gentoo": [ + + ], + "ubuntu": [ + + ], + "redhat": [ + + ] + }, + "suggestions": { + + }, + "license": "Apache 2.0", + "conflicting": { + + }, + "long_description": "= DESCRIPTION:\n\nInstalls Ruby and related packages.\n\n= REQUIREMENTS:\n\n== Platform:\n\nTested on Ubuntu 10.04. Debian and Gentoo should also work fully. CentOS, Red Hat, Fedora and Arch are partially supported.\n\n= ATTRIBUTES:\n\n* +languages[:ruby][:default_version]+ - The Ruby version to install with the ruby recipe and create symlinks for with the symlinks recipe.\n\n= USAGE:\n\nPrevious versions of this cookbook gave you no control over which version of Ruby would be installed. We are now in the middle of an awkward period where you are equally likely to want 1.8 or 1.9. You may even want both. This is now catered for. To install specific versions side-by-side, use the 1.8, 1.9 or 1.9.1 recipes. The ruby recipe will install the version specified by +languages[:ruby][:default_version]+. If you want to do something other than install these packages, the +ruby_packages+ definition is provided as a wrapper around the package resource. Just specify the version number.\n\nFor example, to use the default recipe in a role named \"base\", use 'ruby' in the run list and set the +languages[:ruby][:default_version]+ attribute:\n\n {\n \"name\": \"base\",\n \"description\": \"Base role is applied to all systems\",\n \"json_class\": \"Chef::Role\",\n \"default_attributes\": {\n },\n \"override_attributes\": {\n \"languages\": {\n \"ruby\": {\n \"default_version\": \"1.8\"\n }\n }\n },\n \"chef_type\": \"role\",\n \"run_list\": [\n \"recipe[ruby]\"\n ]\n }\n\nMany scripts, including those provided by Rails, don't ask for a particular version of Ruby such as \"ruby1.8\" and simply look for \"ruby\" instead. Sometimes a symlink is provided and sometimes the executable is simply called \"ruby\" in the first place but generally speaking, it is difficult to predict this behaviour, especially when Ruby Gems is thrown into the mix. The symlinks recipe seeks to relieve you of this headache by creating symlinks for the common executables pointing to the Ruby version specified by +languages[:ruby][:default_version]+. This is also available as a definition called +ruby_symlinks+, which is a wrapper around the link resource. As before, just specify the version number. Non-symlinks will not be overwritten unless you set force to true. You can also set a path other than /usr/bin if necessary.\n\n*IMPORTANT!* Only Ubuntu, Debian and Gentoo support installing a specific Ruby version at all. yum-based distributions install 1.8 by default but require you to give the full package version otherwise. Maybe some magic could be added to Chef? Arch installs 1.9.2 by default but 1.8 is only available from AUR. Additionally, Ubuntu and Debian group 1.9.2 with 1.9.1 while Gentoo lumps all 1.9 releases together.\n\n= LICENSE and AUTHOR:\n\nAuthor:: Joshua Timberman (), James Le Cuirot ()\n\nCopyright:: 2009-2010, Opscode, Inc; 2010, FindsYou Limited\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", + "providing": { + + }, + "description": "Installs Ruby and related packages", + "version": "0.9.0", + "maintainer": "Opscode, Inc.", + "replacing": { + + }, + "attributes": { + "languages/ruby/default_version": { + "required": "optional", + "calculated": false, + "choice": [ + "1.8", + "1.9", + "1.9.1" + ], + "default": "1.8", + "type": "string", + "recipes": [ + "ruby", + "symlinks" + ], + "display_name": "Default Ruby version", + "description": "The Ruby version to install with the ruby recipe and create symlinks for with the symlinks recipe. Unfortunately this setting only works fully on Ubuntu, Debian and Gentoo." + } + }, + "maintainer_email": "cookbooks@opscode.com", + "name": "ruby", + "recipes": { + "symlinks": "Installs symlinks for the default Ruby version", + "1.9.1": "Installs Ruby 1.9.1 and related packages", + "1.8": "Installs Ruby 1.8 and related packages", + "1.9": "Installs Ruby 1.9 and related packages", + "ruby": "Installs Ruby and related packages" + }, + "groupings": { + + }, + "dependencies": { + + }, + "recommendations": { + + } +} \ No newline at end of file diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/metadata.rb new file mode 100644 index 00000000000..9ab210d4902 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/metadata.rb @@ -0,0 +1,23 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs Ruby and related packages" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.9.0" + +recipe "ruby", "Installs Ruby and related packages" +recipe "1.8", "Installs Ruby 1.8 and related packages" +recipe "1.9", "Installs Ruby 1.9 and related packages" +recipe "1.9.1", "Installs Ruby 1.9.1 and related packages" +recipe "symlinks", "Installs symlinks for the default Ruby version" + +attribute "languages/ruby/default_version", + :display_name => "Default Ruby version", + :recipes => [ "ruby", "symlinks" ], + :choice => [ "1.8", "1.9", "1.9.1" ], + :default => "1.8", + :description => "The Ruby version to install with the ruby recipe and create symlinks for with the symlinks recipe. Unfortunately this setting only works fully on Ubuntu, Debian and Gentoo." + +%w{ centos redhat fedora ubuntu debian arch gentoo }.each do |os| + supports os +end diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.8.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.8.rb new file mode 100644 index 00000000000..559db9dc97d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.8.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: ruby +# Recipe:: 1.8 +# +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ruby_packages "1.8" diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.9.1.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.9.1.rb new file mode 100644 index 00000000000..e3e8cfa2eca --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.9.1.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: ruby +# Recipe:: 1.9.1 +# +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ruby_packages "1.9.1" diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.9.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.9.rb new file mode 100644 index 00000000000..70bfe21e862 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/1.9.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: ruby +# Recipe:: 1.9 +# +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ruby_packages "1.9" diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/default.rb new file mode 100644 index 00000000000..9a7964e2e4b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/default.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: ruby +# Recipe:: default +# +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ruby_packages node[:languages][:ruby][:default_version] || "1.8" diff --git a/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/symlinks.rb b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/symlinks.rb new file mode 100644 index 00000000000..60ef6cfd8b7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/ruby/recipes/symlinks.rb @@ -0,0 +1,20 @@ +# +# Cookbook Name:: ruby +# Recipe:: symlinks +# +# Copyright 2010, FindsYou Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +ruby_symlinks node[:languages][:ruby][:default_version] || "1.8" diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/runit/attributes/default.rb new file mode 100644 index 00000000000..143b4203be7 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/attributes/default.rb @@ -0,0 +1,31 @@ +# +# Cookbook Name:: runit +# Attribute File:: sv_bin +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case platform +when "ubuntu","debian" + set[:runit][:sv_bin] = "/usr/bin/sv" + set[:runit][:chpst_bin] = "/usr/bin/chpst" + set[:runit][:service_dir] = "/etc/service" + set[:runit][:sv_dir] = "/etc/sv" +when "gentoo" + set[:runit][:sv_bin] = "/usr/bin/sv" + set[:runit][:chpst_bin] = "/usr/bin/chpst" + set[:runit][:service_dir] = "/etc/service" + set[:runit][:sv_dir] = "/var/service" +end diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/definitions/runit_service.rb b/crowbar/change-image/dell/chef/cookbooks/runit/definitions/runit_service.rb new file mode 100644 index 00000000000..326f9e2f42e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/definitions/runit_service.rb @@ -0,0 +1,159 @@ +# +# Cookbook Name:: runit +# Definition:: runit_service +# +# Copyright 2008-2009, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +define :runit_service, :directory => nil, :only_if => false, :finish_script => false, :control => [], :run_restart => true, :active_directory => nil, :owner => "root", :group => "root", :template_name => nil, :start_command => "start", :stop_command => "stop", :restart_command => "restart", :status_command => "status", :options => Hash.new, :env => Hash.new do + include_recipe "runit" + + params[:directory] ||= node[:runit][:sv_dir] + params[:active_directory] ||= node[:runit][:service_dir] + params[:template_name] ||= params[:name] + + sv_dir_name = "#{params[:directory]}/#{params[:name]}" + service_dir_name = "#{params[:active_directory]}/#{params[:name]}" + params[:options].merge!(:env_dir => "#{sv_dir_name}/env") unless params[:env].empty? + + directory sv_dir_name do + owner params[:owner] + group params[:group] + mode 0755 + action :create + end + + directory "#{sv_dir_name}/log" do + owner params[:owner] + group params[:group] + mode 0755 + action :create + end + + directory "#{sv_dir_name}/log/main" do + owner params[:owner] + group params[:group] + mode 0755 + action :create + end + + template "#{sv_dir_name}/run" do + owner params[:owner] + group params[:group] + mode 0755 + source "sv-#{params[:template_name]}-run.erb" + cookbook params[:cookbook] if params[:cookbook] + if params[:options].respond_to?(:has_key?) + variables :options => params[:options] + end + end + + template "#{sv_dir_name}/log/run" do + owner params[:owner] + group params[:group] + mode 0755 + source "sv-#{params[:template_name]}-log-run.erb" + cookbook params[:cookbook] if params[:cookbook] + if params[:options].respond_to?(:has_key?) + variables :options => params[:options] + end + end + + unless params[:env].empty? + directory "#{sv_dir_name}/env" do + mode 0755 + action :create + end + + params[:env].each do |var, value| + file "#{sv_dir_name}/env/#{var}" do + content value + end + end + end + + if params[:finish_script] + template "#{sv_dir_name}/finish" do + owner params[:owner] + group params[:group] + mode 0755 + source "sv-#{params[:template_name]}-finish.erb" + cookbook params[:cookbook] if params[:cookbook] + if params[:options].respond_to?(:has_key?) + variables :options => params[:options] + end + end + end + + unless params[:control].empty? + directory "#{sv_dir_name}/control" do + owner params[:owner] + group params[:group] + mode 0755 + action :create + end + + params[:control].each do |signal| + template "#{sv_dir_name}/control/#{signal}" do + owner params[:owner] + group params[:group] + mode 0755 + source "sv-#{params[:template_name]}-control-#{signal}.erb" + cookbook params[:cookbook] if params[:cookbook] + if params[:options].respond_to?(:has_key?) + variables :options => params[:options] + end + end + end + end + + if params[:active_directory] == node[:runit][:service_dir] + link "/etc/init.d/#{params[:name]}" do + to node[:runit][:sv_bin] + end + end + + unless node[:platform] == "gentoo" + link service_dir_name do + to sv_dir_name + end + end + + ruby_block "supervise_#{params[:name]}_sleep" do + block do + Chef::Log.debug("Waiting until named pipe #{sv_dir_name}/supervise/ok exists.") + (1..10).each {|i| sleep 1 unless ::FileTest.pipe?("#{sv_dir_name}/supervise/ok") } + end + not_if { FileTest.pipe?("#{sv_dir_name}/supervise/ok") } + end + + service params[:name] do + control_cmd = node[:runit][:sv_bin] + if params[:owner] + control_cmd = "#{node[:runit][:chpst_bin]} -u #{params[:owner]} #{control_cmd}" + end + provider Chef::Provider::Service::Init + supports :restart => true, :status => true + start_command "#{control_cmd} #{params[:start_command]} #{service_dir_name}" + stop_command "#{control_cmd} #{params[:stop_command]} #{service_dir_name}" + restart_command "#{control_cmd} #{params[:restart_command]} #{service_dir_name}" + status_command "#{control_cmd} #{params[:status_command]} #{service_dir_name}" + if params[:run_restart] + subscribes :restart, resources(:template => "#{sv_dir_name}/run"), :delayed + end + action :nothing + end + +end diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/files/default/runit.seed b/crowbar/change-image/dell/chef/cookbooks/runit/files/default/runit.seed new file mode 100644 index 00000000000..64929201ef9 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/files/default/runit.seed @@ -0,0 +1 @@ +runit runit/signalinit boolean true diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/files/default/runsvdir b/crowbar/change-image/dell/chef/cookbooks/runit/files/default/runsvdir new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-6.10/runsvdir b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-6.10/runsvdir new file mode 100644 index 00000000000..4040e34f10d --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-6.10/runsvdir @@ -0,0 +1,6 @@ +start on runlevel-2 +start on runlevel-3 +start on runlevel-4 +start on runlevel-5 +stop on shutdown +respawn /usr/sbin/runsvdir-start diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-7.04/runsvdir b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-7.04/runsvdir new file mode 100644 index 00000000000..ee173c9c094 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-7.04/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-7.10/runsvdir b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-7.10/runsvdir new file mode 100644 index 00000000000..ee173c9c094 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-7.10/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-8.04/runsvdir b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-8.04/runsvdir new file mode 100644 index 00000000000..ee173c9c094 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/files/ubuntu-8.04/runsvdir @@ -0,0 +1,7 @@ +start on runlevel 2 +start on runlevel 3 +start on runlevel 4 +start on runlevel 5 +stop on shutdown +respawn +exec /usr/sbin/runsvdir-start diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/runit/metadata.rb new file mode 100644 index 00000000000..c748b9351be --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/metadata.rb @@ -0,0 +1,37 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs runit and provides runit_service definition" +version "0.14.2" + +recipe "runit", "Installs and configures runit" + +%w{ ubuntu debian gentoo }.each do |os| + supports os +end + +attribute "runit", + :display_name => "Runit", + :description => "Hash of runit attributes", + :type => "hash" + +attribute "runit/sv_bin", + :display_name => "Runit sv bin", + :description => "Location of the sv binary", + :default => "/usr/bin/sv" + +attribute "runit/chpst_bin", + :display_name => "Runit chpst bin", + :description => "Location of the chpst binary", + :default => "/usr/bin/chpst" + +attribute "runit/service_dir", + :display_name => "Runit service directory", + :description => "Symlinks to services managed under runit", + :default => "/etc/service" + +attribute "runit/sv_dir", + :display_name => "Runit sv directory", + :description => "Location of services managed by runit", + :default => "/etc/sv" + diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/runit/recipes/default.rb new file mode 100644 index 00000000000..25f818e988b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/recipes/default.rb @@ -0,0 +1,72 @@ +# +# Cookbook Name:: runit +# Recipe:: default +# +# Copyright 2008-2010, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case node[:platform] +when "debian","ubuntu", "gentoo" + execute "start-runsvdir" do + command value_for_platform( + "debian" => { "default" => "runsvdir-start" }, + "ubuntu" => { "default" => "start runsvdir" }, + "gentoo" => { "default" => "/etc/init.d/runit-start start" } + ) + action :nothing + end + + execute "runit-hup-init" do + command "telinit q" + only_if "grep ^SV /etc/inittab" + action :nothing + end + + if platform? "gentoo" + template "/etc/init.d/runit-start" do + source "runit-start.sh.erb" + mode 0755 + end + end + + package "runit" do + action :install + if platform?("ubuntu", "debian") + response_file "runit.seed" + end + notifies value_for_platform( + "debian" => { "4.0" => :run, "default" => :nothing }, + "ubuntu" => { + "default" => :nothing, + "9.04" => :run, + "8.10" => :run, + "8.04" => :run }, + "gentoo" => { "default" => :run } + ), resources(:execute => "start-runsvdir"), :immediately + notifies value_for_platform( + "debian" => { "squeeze/sid" => :run, "default" => :nothing }, + "default" => :nothing + ), resources(:execute => "runit-hup-init"), :immediately + end + + if node[:platform] =~ /ubuntu/i && node[:platform_version].to_f <= 8.04 + cookbook_file "/etc/event.d/runsvdir" do + source "runsvdir" + mode 0644 + notifies :run, resources(:execute => "start-runsvdir"), :immediately + only_if do ::File.directory?("/etc/event.d") end + end + end +end diff --git a/crowbar/change-image/dell/chef/cookbooks/runit/templates/gentoo/runit-start.sh.erb b/crowbar/change-image/dell/chef/cookbooks/runit/templates/gentoo/runit-start.sh.erb new file mode 100644 index 00000000000..a6c11b3d068 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/runit/templates/gentoo/runit-start.sh.erb @@ -0,0 +1,32 @@ +#!/sbin/runscript +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +depend() { + after net +} + +start() { + ebegin "Starting runsvdir" + start-stop-daemon --start --exec /usr/bin/runsvdir \ + --background --make-pidfile \ + --pidfile /var/run/runsvdir.pid -- <%= node.runit.sv_dir %> + eend $? +} + +stop() { + local ret1 ret2 + ebegin "Stopping runsvdir" + start-stop-daemon --stop --oknodo --pidfile /var/run/runsvdir.pid + ret1=$? + eend ${ret1} + + ebegin "Stopping services and logging" + sv shutdown -w 10 <%= node.runit.sv_dir %>/* + ret2=$? + eend ${ret2} + + return $((ret1+ret2)) +} + diff --git a/crowbar/change-image/dell/chef/cookbooks/sudo/README.md b/crowbar/change-image/dell/chef/cookbooks/sudo/README.md new file mode 100644 index 00000000000..5d4531ddd5e --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/sudo/README.md @@ -0,0 +1,75 @@ +DESCRIPTION +=========== + +This cookbook installs sudo and configures the /etc/sudoers file. + +REQUIREMENTS +============ + +Requires that the platform has a package named sudo and the sudoers file is /etc/sudoers. + +ATTRIBUTES +========== + +The following attributes are set to blank arrays: + + node['authorization']['sudo']['groups'] + node['authorization']['sudo']['users'] + +They are passed into the sudoers template which iterates over the values to add sudo permission to the specified users and groups. + +If you prefer to use passwordless sudo just set the following attribute to true: + + node['authorization']['sudo']['passwordless'] + +USAGE +===== + +To use this cookbook, set the attributes above on the node via a role or the node object itself. In a role.rb: + + "authorization" => { + "sudo" => { + "groups" => ["admin", "wheel", "sysadmin"], + "users" => ["jerry", "greg"], + "passwordless" => true + } + } + +In JSON (role.json or on the node object): + + "authorization": { + "sudo": { + "groups": [ + "admin", + "wheel", + "sysadmin" + ], + "users": [ + "jerry", + "greg" + ], + "passwordless": true + } + } + +Note that the template for the sudoers file has the group "sysadmin" with ALL:ALL permission, though the group by default does not exist. + +LICENSE AND AUTHOR +================== + +Author:: Adam Jacob +Author:: Seth Chisamore + +Copyright 2009-2011, Opscode, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crowbar/change-image/dell/chef/cookbooks/sudo/attributes/default.rb b/crowbar/change-image/dell/chef/cookbooks/sudo/attributes/default.rb new file mode 100644 index 00000000000..5c254b35080 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/sudo/attributes/default.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: sudo +# Attribute File:: sudoers +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +default['authorization']['sudo']['groups'] = Array.new +default['authorization']['sudo']['users'] = Array.new +default['authorization']['sudo']['passwordless'] = false diff --git a/crowbar/change-image/dell/chef/cookbooks/sudo/metadata.rb b/crowbar/change-image/dell/chef/cookbooks/sudo/metadata.rb new file mode 100644 index 00000000000..3eb58d3056c --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/sudo/metadata.rb @@ -0,0 +1,39 @@ +maintainer "Opscode, Inc." +maintainer_email "cookbooks@opscode.com" +license "Apache 2.0" +description "Installs sudo and configures /etc/sudoers" +version "1.0.0" + +recipe "sudo", "Installs sudo and configures /etc/sudoers" + +%w{redhat centos fedora ubuntu debian freebsd}.each do |os| + supports os +end + +attribute "authorization", + :display_name => "Authorization", + :description => "Hash of Authorization attributes", + :type => "hash" + +attribute "authorization/sudoers", + :display_name => "Authorization Sudoers", + :description => "Hash of Authorization/Sudoers attributes", + :type => "hash" + +attribute "authorization/sudoers/users", + :display_name => "Sudo Users", + :description => "Users who are allowed sudo ALL", + :type => "array", + :default => "" + +attribute "authorization/sudoers/groups", + :display_name => "Sudo Groups", + :description => "Groups who are allowed sudo ALL", + :type => "array", + :default => "" + +attribute "authorization/sudoers/passwordless", + :display_name => "Passwordless Sudo", + :description => "", + :type => "string", + :default => "false" diff --git a/crowbar/change-image/dell/chef/cookbooks/sudo/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/sudo/recipes/default.rb new file mode 100644 index 00000000000..da186d80b06 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/sudo/recipes/default.rb @@ -0,0 +1,34 @@ +# +# Cookbook Name:: sudo +# Recipe:: default +# +# Copyright 2008-2011, Opscode, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package "sudo" do + action :upgrade +end + +template "/etc/sudoers" do + source "sudoers.erb" + mode 0440 + owner "root" + group "root" + variables( + :sudoers_groups => node['authorization']['sudo']['groups'], + :sudoers_users => node['authorization']['sudo']['users'], + :passwordless => node['authorization']['sudo']['passwordless'] + ) +end diff --git a/crowbar/change-image/dell/chef/cookbooks/sudo/templates/default/sudoers.erb b/crowbar/change-image/dell/chef/cookbooks/sudo/templates/default/sudoers.erb new file mode 100644 index 00000000000..fe55f722dac --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/sudo/templates/default/sudoers.erb @@ -0,0 +1,23 @@ +# +# /etc/sudoers +# +# Generated by Chef for <%= node[:fqdn] %> +# + +#Defaults !lecture,tty_tickets,!fqdn +Defaults env_reset + +# User privilege specification +root ALL=(ALL) ALL + +<% @sudoers_users.each do |user| -%> +<%= user %> ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL +<% end -%> + +# Members of the sysadmin group may gain root privileges +%sysadmin ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL + +<% @sudoers_groups.each do |group| -%> +# Members of the group '<%= group %>' may gain root privileges +%<%= group %> ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL +<% end -%> diff --git a/crowbar/change-image/dell/chef/cookbooks/utils/metadata.json b/crowbar/change-image/dell/chef/cookbooks/utils/metadata.json new file mode 100644 index 00000000000..9e3a157b284 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/utils/metadata.json @@ -0,0 +1,36 @@ +{ + "providing": { + }, + "attributes": { + }, + "replacing": { + }, + "dependencies": { + }, + "groupings": { + }, + "recommendations": { + }, + "platforms": { + "debian": [ + + ], + "ubuntu": [ + + ] + }, + "license": "Apache 2.0", + "version": "0.9.5", + "maintainer": "Dell, Inc.", + "suggestions": { + }, + "recipes": { + "utils": "Utilities to help other cookbooks" + }, + "maintainer_email": "crowbar@dell.com", + "name": "utils", + "conflicting": { + }, + "description": "Provides utilities for other recipes: line", + "long_description": "" +} diff --git a/crowbar/change-image/dell/chef/cookbooks/utils/providers/line.rb b/crowbar/change-image/dell/chef/cookbooks/utils/providers/line.rb new file mode 100644 index 00000000000..997a100a73b --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/utils/providers/line.rb @@ -0,0 +1,52 @@ + +action :add do + filename = "/tmp/#{new_resource.file.gsub("/","_")}.lock" + f = ::File.new(filename, ::File::RDWR|::File::CREAT, 0644) + rc = false + count = 0 + while rc == false do + count = count + 1 + rc = f.flock(::File::LOCK_EX|::File::LOCK_NB) + sleep 1 if rc == false + end + + system "/bin/egrep -q '^#{new_resource.name}$' '#{new_resource.file}'" + ret=$? + if ret != 0 + b = bash "add #{new_resource.name}" do + code "/bin/echo '#{new_resource.name}' >> '#{new_resource.file}'" + end + b.action(:run) + new_resource.updated_by_last_action(true) + end + + f.flock(::File::LOCK_UN) + f.close +end + +action :remove do + filename = "/tmp/#{new_resource.file.gsub("/","_")}.lock" + f = ::File.new(filename, ::File::RDWR|::File::CREAT, 0644) + rc = false + count = 0 + while rc == false do + count = count + 1 + rc = f.flock(::File::LOCK_EX|::File::LOCK_NB) + sleep 1 if rc == false + end + + system "/bin/egrep -q '^#{new_resource.name}$' '#{new_resource.file}'" + ret=$? + if ret == 0 + line_str = new_resource.name.gsub("\"", "\\\"").gsub("/", "\\/").gsub("'", "\\'") + b = bash "remove #{new_resource.name}" do + code "/usr/bin/perl -ni -e 'print unless /^#{line_str}$/' '#{new_resource.file}'" + end + b.action(:run) + new_resource.updated_by_last_action(true) + end + + f.flock(::File::LOCK_UN) + f.close +end + diff --git a/crowbar/change-image/dell/chef/cookbooks/utils/recipes/default.rb b/crowbar/change-image/dell/chef/cookbooks/utils/recipes/default.rb new file mode 100644 index 00000000000..e360f985971 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/utils/recipes/default.rb @@ -0,0 +1,27 @@ +# Copyright (c) 2011 Dell Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# set some shortcut variables. +@@centos = @@ubuntu = false +platform = node[:platform] +case platform + when "centos", "redhat" + @@centos = true + when "ubuntu" + @@ubuntu = true +end + +log("running on OS:[#{platform}] on #{node[:dmi][:system][:product_name]} hardware") { level :info} + diff --git a/crowbar/change-image/dell/chef/cookbooks/utils/resources/line.rb b/crowbar/change-image/dell/chef/cookbooks/utils/resources/line.rb new file mode 100644 index 00000000000..36844ab7893 --- /dev/null +++ b/crowbar/change-image/dell/chef/cookbooks/utils/resources/line.rb @@ -0,0 +1,6 @@ + +actions :add, :remove + +attribute :name, :kind_of => String, :name_attribute => true +attribute :file, :kind_of => String + diff --git a/crowbar/change-image/dell/common_install.sh b/crowbar/change-image/dell/common_install.sh new file mode 100755 index 00000000000..59a6fc9f7be --- /dev/null +++ b/crowbar/change-image/dell/common_install.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# +# This script is called by the other install scripts to layout the openstack +# software + dell pieces. +# +# Requires: +# /tftpboot/ubuntu_dvd is populated with a tarball of the dvd and this file. +# + +BASEDIR="/tftpboot/ubuntu_dvd" + +# explode the dvd.tgz +cd $BASEDIR +if [[ -e $BASEDIR/dvd.tar ]]; then + tar -xf "$BASEDIR/dvd.tar" +elif [[ -e $BASEDIR/dvd.tgz ]]; then + tar -zxf "$BASEDIR/dvd.tgz" +fi + +# Make sure /opt is created +mkdir -p /opt/dell/bin + +# Copy the dell parts into a hidden install directory. +cd /opt +cp -r /$BASEDIR/dell .dell-install + +# Make a destination for dell finishing scripts + +finishing_scripts=(update_hostname.sh validate_data_bag.rb \ + validate_bags.rb blocking_chef_client.sh looper_chef_client.sh single_chef_client.sh \ + install_barclamp.sh barclamp_lib.rb gather_logs.sh gather_cli.sh) +( cd /opt/.dell-install; cp "${finishing_scripts[@]}" /opt/dell/bin; ) + +# "Install h2n for named management" +cd /opt/dell/ +tar -zxf /tftpboot/ubuntu_dvd/extra/h2n.tar.gz +ln -s /opt/dell/h2n-2.56/h2n /opt/dell/bin/h2n + +cp -r /opt/.dell-install/openstack_manager /opt/dell + +# Make a destination for switch configs +mkdir -p /opt/dell/switch +cp /opt/.dell-install/*.stk /opt/dell/switch + +# Install dell code +cd /opt/.dell-install + +# put the chef files in place +cp -r chef /opt/dell +cp rsyslog.d/* /etc/rsyslog.d/ + +# Install barclamps for now +cd barclamps +for i in *; do + [[ -d $i ]] || continue + cd "$i" + ( cd chef; cp -r * /opt/dell/chef ) + ( cd app; cp -r * /opt/dell/openstack_manager/app ) + ( cd config; cp -r * /opt/dell/openstack_manager/config ) + ( cd command_line; cp * /opt/dell/bin ) + ( cd public ; cp -r * /opt/dell/openstack_manager/public ) + cd .. +done +cd .. + +# Make sure the bin directory is executable +chmod +x /opt/dell/bin/* + +# Apparmor seems to have something to do with the +# apache hangs. Disable it for now for testing. +update-rc.d apparmor disable + +# put the apt files in place +cp apt.conf sources.list /etc/apt + +# Make sure the ownerships are correct +chown -R openstack.admin /opt/dell + +# +# Make sure the permissions are right +# Copy from a cd so that means most things are read-only which is fine, except for these. +# +chmod 755 /opt/dell/chef/data_bags/crowbar +chmod 644 /opt/dell/chef/data_bags/crowbar/* +chmod 755 /opt/dell/openstack_manager/db +chmod 644 /opt/dell/openstack_manager/db/* +chmod 755 /opt/dell/openstack_manager/tmp +chmod -R +w /opt/dell/openstack_manager/tmp/* +chmod 755 /opt/dell/openstack_manager/public/stylesheets + +# Get out of the directories. +cd + +# Look for any crowbar specific kernel parameters +for s in $(cat /proc/cmdline); do + VAL=${s#*=} # everything after the first = + case ${s%%=*} in # everything before the first = + crowbar.hostname) CHOSTNAME=$VAL;; + crowbar.url) CURL=$VAL;; + crowbar.use_serial_console) + sed -i "s/\"use_serial_console\": .*,/\"use_serial_console\": $VAL,/" /opt/dell/chef/data_bags/crowbar/bc-template-provisioner.json;; + crowbar.debug.logdest) + echo "*.* $VAL" >> /etc/rsyslog.d/00-crowbar-debug.conf + mkdir -p "$BASEDIR/rsyslog.d" + echo "*.* $VAL" >> "$BASEDIR/rsyslog.d/00-crowbar-debug.conf" + ;; + crowbar.authkey) + mkdir -p "/root/.ssh" + printf "$VAL\n" >>/root/.ssh/authorized_keys + cp /root/.ssh/authorized_keys "$BASEDIR/authorized_keys" + ;; + esac +done + +if [[ $CHOSTNAME ]]; then + + cat > /install_system.sh <"$loc/${f##*/}" +done + +cd "${loc}" +cp /tftpboot/ubuntu_dvd/extra/gems/net-http-digest*.gem . +cat >install.sh <<"EOF" +#!/bin/bash + +[[ $1 && -d $1 && -w $1 ]] || { + echo "Please pass a directory to install the Crowbar CLI to." + exit 1 +} +gem install net-http-digest_auth --no-ri --no rdoc --local net-http-*.gem || { + echo "Could not install the net-http-digest_auth gem." + echo "Make sure gem is installed, and you have permissions to install gems." + exit 1 +} +rm *.gem +cp * "$1" + +EOF +chmod 755 * +tar czf "$dest" crowbar* barclamp* *.sh *.gem +cd / +rm -rf "${loc}" \ No newline at end of file diff --git a/crowbar/change-image/dell/gather_logs.sh b/crowbar/change-image/dell/gather_logs.sh new file mode 100755 index 00000000000..e7ae6deac5a --- /dev/null +++ b/crowbar/change-image/dell/gather_logs.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +if [[ -f /etc/crowbar.install.key ]]; then + export CROWBAR_KEY=$(cat /etc/crowbar.install.key) +fi +mkdir -p /tmp/crowbar-logs +tarname="$1" +targetdir="/opt/dell/openstack_manager/public/logs" +( flock -s 200 + logdir=$(mktemp -d "/tmp/crowbar-logs/$tarname-XXXXX") + mkdir -p "$logdir" + mkdir -p "$targetdir" + cd "$logdir" + hostname_re='"([^"]+)"' + sshopts=(-q -o 'StrictHostKeyChecking no' + -o 'UserKnownHostsFile /dev/null') + logs=(/var/log /etc) + for node in $(sudo -H knife node list); do + [[ $node =~ $hostname_re ]] || continue + node=${BASH_REMATCH[1]} + mkdir -p "${node%%.*}" + tarfile="${node%%.*}-${tarname}.tar.gz" + ( cd "${node%%.*}" + [[ $node =~ ^admin ]] && { + curlargs=(-o /dev/null -D - --connect-timeout 30 --max-time 120) + [[ $CROWBAR_KEY ]] && curlargs+=(--digest -u "$CROWBAR_KEY") + for to_get in nodes proposals roles; do + curl "${curlargs[@]}" "http://$node:3000/$to_get" || : + done + logs+=(/opt/dell/openstack_manager/db /opt/dell/openstack_manager/log /install-logs) + } + sudo ssh "${sshopts[@]}" "${node}" \ + tar czf "/tmp/$tarfile" "${logs[@]}" + sudo scp "${sshopts[@]}" "${node}:/tmp/${tarfile}" "${tarfile}" + )& + done &>/dev/null + wait + cd .. + tar cf "$targetdir/crowbar-logs-${tarname}.tar" . &>/dev/null + rm -rf "$logdir" +) 200>/tmp/crowbar-logs/.lock \ No newline at end of file diff --git a/crowbar/change-image/dell/install_barclamp.sh b/crowbar/change-image/dell/install_barclamp.sh new file mode 100755 index 00000000000..b6c88c55575 --- /dev/null +++ b/crowbar/change-image/dell/install_barclamp.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +# Install barclamps for now +cd "$1/chef" + +cp -r * /opt/dell/chef + +cd cookbooks; knife cookbook upload -o . -a; cd .. + +cd data_bags/crowbar; +for i in *.json; do + knife data bag from file crowbar $i +done +cd ../.. # now in $1/chef again +cd roles +for i in *.rb; do + knife role from file $i +done +cd ../.. # now just in $1 +cd app; cp -r * /opt/dell/openstack_manager/app; cd .. +cd command_line; cp * /opt/dell/bin; cd .. +cp -r public /opt/dell/openstack_manager; + +if [[ -f /etc/redhat-release ]]; then + service httpd reload +else + service apache2 reload +fi + +cd - + diff --git a/crowbar/change-image/dell/looper_chef_client.sh b/crowbar/change-image/dell/looper_chef_client.sh new file mode 100755 index 00000000000..8ba5819625e --- /dev/null +++ b/crowbar/change-image/dell/looper_chef_client.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +[[ -f /tmp/deploying ]] && exit 0 + +lockfile="/tmp/looper-chef-client.lock" + +if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then + trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT + + while true; do + rm -f /tmp/chef-client.run + + /opt/dell/bin/blocking_chef_client.sh + + while [[ ! -f /tmp/chef-client.run ]] + do + sleep 1 + done + done + + # Ideally these won't be hit. + rm -f "$lockfile" + trap - INT TERM EXIT + exit 0 +else + touch /tmp/chef-client.run +fi + diff --git a/crowbar/change-image/dell/openstack_manager/Rakefile b/crowbar/change-image/dell/openstack_manager/Rakefile new file mode 100644 index 00000000000..3bb0e8592a4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/Rakefile @@ -0,0 +1,10 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require(File.join(File.dirname(__FILE__), 'config', 'boot')) + +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +require 'tasks/rails' diff --git a/crowbar/change-image/dell/openstack_manager/app/controllers/application_controller.rb b/crowbar/change-image/dell/openstack_manager/app/controllers/application_controller.rb new file mode 100644 index 00000000000..8604acad4a5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/controllers/application_controller.rb @@ -0,0 +1,96 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# + +require 'uri' + +# Filters added to this controller apply to all controllers in the application. +# Likewise, all the methods added will be available for all controllers. +class ApplicationController < ActionController::Base + + # Basis for the reflection/help system. + + # First, a place to stash the help contents. + # Using a class_inheritable_accessor ensures that + # these contents are inherited by children, but can be + # overridden or appended to by child classes without messing up + # the contents we are building here. + class_inheritable_accessor :help_contents + self.help_contents = [] + + # Class method for adding method-specific help/API information + # for each method we are going to expose to the CLI. + # Since it is a class method, it will not be bothered by the Rails + # trying to expose it to everything else, and we can call it to build + # up our help contents at class creation time instead of instance creation + # time, so there is minimal overhead. + # Since we are just storing an arrray of singleton hashes, adding more + # user-oriented stuff (descriptions, exmaples, etc.) should not be a problem. + def self.add_help(method,args=[],http_method=[:get]) + # if we were passed multiple http_methods, build an entry for each. + # This assumes that they all take the same parameters, if they do not + # you should call add_help for each different set of parameters that the + # method/http_method combo can take. + http_method.each { |m| + self.help_contents = self.help_contents.push({ + method => { + "args" => args, + "http_method" => m + } + }) + } + end + + #helper :all # include all helpers, all the time + + protect_from_forgery # See ActionController::RequestForgeryProtection for details + + def self.set_layout(template = "application") + layout proc { |controller| + if controller.is_ajax? + return nil + end + template + } + end + + def is_ajax? + request.xhr? + end + + add_help(:help) + def help + render :json => { self.controller_name => self.help_contents.collect { |m| + res = {} + m.each { |k,v| + # sigh, we cannot resolve url_for at class definition time. + # I suppose we have to do it at runtime. + url=URI::unescape(url_for({ :action => k, + :controller => self.controller_name, + + }.merge(v["args"].inject({}) {|acc,x| + acc.merge({x.to_s => "(#{x.to_s})"}) + } + ) + )) + res.merge!({ k.to_s => v.merge({"url" => url})}) + } + res + } + } + end + set_layout +end diff --git a/crowbar/change-image/dell/openstack_manager/app/controllers/barclamp_controller.rb b/crowbar/change-image/dell/openstack_manager/app/controllers/barclamp_controller.rb new file mode 100644 index 00000000000..e6d64f15c19 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/controllers/barclamp_controller.rb @@ -0,0 +1,349 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +require 'chef' +require 'json' + +class BarclampController < ApplicationController + + before_filter :controller_to_barclamp + + def controller_to_barclamp + @bc_name = params[:barclamp] || params[:controller] + @service_object.bc_name = @bc_name + end + + self.help_contents = Array.new(superclass.help_contents) + def initialize + super() + @service_object = ServiceObject.new logger + end + + # Barclamp List (generic) + add_help(:barclamp_index) + def barclamp_index + @barclamps = ServiceObject.all + + respond_to do |format| + format.html { render :template => 'barclamp/barclamp_index' } + format.xml { render :xml => @barclamps } + format.json { render :json => @barclamps } + end + end + + # Barclamp Show (specific one) + add_help(:barclamp_show, [:id]) + def barclamp_show + @barclamp = ServiceObject.new logger + @barclamp.bc_name = params[:id] + + respond_to do |format| + format.html { render :template => 'barclamp/barclamp_show' } + format.xml { render :xml => @proposal } + format.json + end + end + + # Barclamp Roles (all) + add_help(:barclamp_roles, []) + def barclamp_roles + @roles = RoleObject.all + @roles = @roles.delete_if { |v| v.nil? or !(v.name =~ /^.*-config-/) } + respond_to do |format| + format.html { render :template => 'barclamp/barclamp_roles' } + format.xml { render :xml => @roles } + format.json { render :json => @roles } + end + end + + # Barclamp Proposals (all) + add_help(:barclamp_proposals, []) + def barclamp_proposals + @proposals = ProposalObject.all + @proposals = @proposals.delete_if { |v| v.nil? or v.id =~ /^#{ProposalObject::BC_PREFIX}/ } + respond_to do |format| + format.html { render :template => 'barclamp/barclamp_proposals' } + format.xml { render :xml => @proposals } + format.json { render :json => @proposals } + end + end + + add_help(:versions) + def versions + ret = @service_object.versions + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + + add_help(:transition, [:id, :name, :state], [:get,:post]) + def transition + id = params[:id] # Provisioner id + state = params[:state] # State of node transitioning + name = params[:name] # Name of node transitioning + + ret = @service_object.transition(id, name, state) + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + + add_help(:index) + def index + ret = @service_object.list_active + @roles = ret[1] + respond_to do |format| + format.html { + @roles.map! { |r| RoleObject.find_role_by_name("#{@bc_name}-config-#{r}") } + render :template => 'barclamp/index' + } + format.xml { + return render :text => @roles, :status => ret[0] if ret[0] != 200 + render :xml => @roles + } + format.json { + return render :text => @roles, :status => ret[0] if ret[0] != 200 + render :json => @roles + } + end + end + + add_help(:show,[:id]) + def show + ret = @service_object.show_active params[:id] + @role = ret[1] + respond_to do |format| + format.html { render :template => 'barclamp/show' } + format.xml { + return render :text => @role, :status => ret[0] if ret[0] != 200 + render :xml => ServiceObject.role_to_proposal(@role, @bc_name) + } + format.json { + return render :text => @role, :status => ret[0] if ret[0] != 200 + render :json => ServiceObject.role_to_proposal(@role, @bc_name) + } + end + end + + add_help(:delete,[:id],[:delete]) + def delete + params[:id] = params[:id] || params[:name] + ret = [500, "Server Problem"] + begin + ret = @service_object.destroy_active(params[:id]) + flash[:notice] = ret[1] if ret[0] >= 300 + flash[:notice] = t('barclamp.show.delete_role_success') if ret[0] == 200 + rescue Exception => e + flash[:notice] = e.message + end + + respond_to do |format| + format.html { + return redirect_to show_barclamp_path(:id => params[:id], :controller => @bc_name) if ret[0] != 200 + redirect_to barclamp_roles_barclamp_path + } + format.xml { + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :xml => {} + } + format.json { + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => {} + } + end + end + + add_help(:status,[],[:get]) + def status + bcs = {} + begin + result = ProposalObject.all + result = result.delete_if { |v| v.id =~ /^#{ProposalObject::BC_PREFIX}/ } + result.each do |role| + bcs[role.barclamp] = (bcs[role.barclamp].nil? ? 1 : bcs[role.barclamp] + 1) + end + render :inline => {:count=>bcs.length, :barclamps=>bcs}.to_json, :cache => false + rescue Exception=>e + count = (e.class.to_s == "Errno::ECONNREFUSED" ? -2 : -1) + Rails.logger.fatal("Failed to iterate over proposals list due to '#{e.message}'") + end + end + + add_help(:elements) + def elements + ret = @service_object.elements + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + + add_help(:element_info,[:id]) + def element_info + ret = @service_object.element_info + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + + add_help(:proposals) + def proposals + ret = @service_object.proposals + @proposals = ret[1] + return render :text => @proposals, :status => ret[0] if ret[0] != 200 + respond_to do |format| + format.html { + @proposals.map! { |p| ProposalObject.find_proposal(@bc_name, p) } + render :template => 'barclamp/proposal_index' + } + format.xml { render :xml => @proposals } + format.json { render :json => @proposals } + end + end + + add_help(:proposal_show,[:id]) + def proposal_show + ret = @service_object.proposal_show params[:id] + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + @proposal = ret[1] + + @attr_raw = params[:attr_raw] || false + @dep_raw = params[:dep_raw] || false + + respond_to do |format| + format.html { render :template => 'barclamp/proposal_show' } + format.xml { render :xml => @proposal.raw_data } + format.json { render :json => @proposal.raw_data } + end + end + + add_help(:proposal_status,[],[:get]) + def proposal_status + proposals = {} + begin + result = ProposalObject.all + result = result.delete_if { |v| v.id =~ /^#{ProposalObject::BC_PREFIX}/ } + result.each do |prop| + proposals["#{prop.barclamp}_#{prop.name}"] = prop.status + end + render :inline => {:proposals=>proposals, :count=>proposals.length}.to_json, :cache => false + rescue Exception=>e + count = (e.class.to_s == "Errno::ECONNREFUSED" ? -2 : -1) + Rails.logger.fatal("Failed to iterate over proposal list due to '#{e.message}'") + # render :inline => {:proposals=>proposals, :count=>count, :error=>e.message}, :cache => false + end + end + + add_help(:proposal_create,[:name],[:put]) + def proposal_create + controller = params[:controller] + orig_id = params[:name] || params[:id] + params[:id] = orig_id + begin + answer = @service_object.proposal_create params + flash[:notice] = answer[0] != 200 ? answer[1] : t('barclamp.proposal_show.create_proposal_success') + rescue Exception => e + flash[:notice] = e.message + end + respond_to do |format| + format.html { + return redirect_to barclamp_show_barclamp_path(controller) if answer[0] != 200 + redirect_to proposal_barclamp_path(:controller => controller, :id => orig_id) + } + format.xml { + return render :text => flash[:notice], :status => answer[0] if answer[0] != 200 + render :xml => answer[1] + } + format.json { + return render :text => flash[:notice], :status => answer[0] if answer[0] != 200 + render :json => answer[1] + } + end + end + + add_help(:proposal_update,[:id],[:post]) + def proposal_update + if params[:submit].nil? # This is RESTFul path + ret = @service_object.proposal_edit params + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + return render :json => ret[1] + else # This is UI. + params[:id] = "bc-#{params[:barclamp]}-#{params[:name]}" + if params[:submit] == t('barclamp.proposal_show.save_proposal') + @proposal = ProposalObject.find_proposal_by_id(params[:id]) + + begin + @proposal["attributes"][params[:barclamp]] = JSON.parse(params[:proposal_attributes]) + @proposal["deployment"][params[:barclamp]] = JSON.parse(params[:proposal_deployment]) + + @service_object.validate_proposal @proposal.raw_data + @proposal.save + flash[:notice] = t('barclamp.proposal_show.save_proposal_success') + rescue Exception => e + flash[:notice] = e.message + end + elsif params[:submit] == t('barclamp.proposal_show.commit_proposal') + begin + answer = @service_object.proposal_commit(params[:name]) + flash[:notice] = answer[1] if answer[0] >= 300 + flash[:notice] = t('barclamp.proposal_show.commit_proposal_success') if answer[0] == 200 + flash[:notice] = "#{t('barclamp.proposal_show.commit_proposal_queued')}: #{answer[1].inspect}" if answer[0] == 202 + rescue Exception => e + flash[:notice] = e.message + end + elsif params[:submit] == t('barclamp.proposal_show.delete_proposal') + begin + answer = @service_object.proposal_delete(params[:name]) + flash[:notice] = answer[1] if answer[0] >= 300 + flash[:notice] = t('barclamp.proposal_show.delete_proposal_success') if answer[0] == 200 + return redirect_to barclamp_proposals_barclamp_path if answer[0] == 200 + rescue Exception => e + flash[:notice] = e.message + end + elsif params[:submit] == t('barclamp.proposal_show.dequeue_proposal') + begin + answer = @service_object.dequeue_proposal(params[:name]) + flash[:notice] = t('barclamp.proposal_show.dequeue_proposal_failure') unless answer + flash[:notice] = t('barclamp.proposal_show.dequeue_proposal_success') if answer + rescue Exception => e + flash[:notice] = e.message + end + else + Rails.logger.warn "Invalid action #{params[:submit]} for #{params[:id]}" + flash[:notice] = "Invalid action #{params[:submit]}" + end + redirect_to proposal_barclamp_path(:controller => params[:barclamp], :id => params[:name]) + end + end + + add_help(:proposal_delete,[:id],[:delete]) + def proposal_delete + ret = @service_object.proposal_delete params[:id] + return render :text => ret[1], :status => ret[0] if ret[0] != 200 + render :json => ret[1] + end + + add_help(:proposal_commit,[:id],[:post]) + def proposal_commit + ret = @service_object.proposal_commit params[:id] + return render :text => ret[1], :status => ret[0] if ret[0] >= 210 + render :json => ret[1], :status => ret[0] + end + + add_help(:proposal_dequeue,[:id],[:post]) + def proposal_dequeue + ret = @service_object.dequeue_proposal params[:id] + return render :text => "Failed to dequeue", :status => 400 unless ret + render :json => {}, :status => 200 if ret + end + +end + diff --git a/crowbar/change-image/dell/openstack_manager/app/controllers/machines_controller.rb b/crowbar/change-image/dell/openstack_manager/app/controllers/machines_controller.rb new file mode 100644 index 00000000000..4198c7c50f1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/controllers/machines_controller.rb @@ -0,0 +1,242 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +require 'chef' + +class MachinesController < ApplicationController + + self.help_contents = Array.new(superclass.help_contents) + + def index + if FileTest.exist? CHEF_CLIENT_KEY + begin + if session[:domain].nil? + session[:domain] = ChefObject.cloud_domain + end + @app = NodeObject.find_all_nodes + rescue + flash.now[:notice] = "ERROR: Could not connect to Chef Server at \"#{CHEF_SERVER_URL}.\"" + @app = [] + end + else + flash.now[:notice] = "ERROR: Could not find Chef Key at \"#{CHEF_CLIENT_KEY}.\"" + end + + respond_to do |format| + format.html + format.json { render :json => @app.map! { |x| x.name } } + end + end + + add_help(:list) + def list + index + end + + add_help(:show,[:name]) + def show + name = params[:name] + if session[:domain].nil? + session[:domain] = ChefObject.cloud_domain + end + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + respond_to do |format| + format.html + format.json { render :json => machine.to_hash } + end + end + end + + add_help(:reinstall,[:name],[:post]) + def reinstall + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.set_state("reinstall") + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + add_help(:reset,[:name],[:post]) + def reset + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.set_state("reset") + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + redirect_to :action => :index + end + + add_help(:identify,[:name],[:post]) + def identify + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.identify + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + add_help(:shutdown,[:name],[:post]) + def shutdown + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.shutdown + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + add_help(:reboot,[:name],[:post]) + def reboot + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.reboot + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + add_help(:poweron,[:name],[:post]) + def poweron + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.poweron + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + add_help(:allocate,[:name],[:post]) + def allocate + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.allocate + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + add_help(:delete,[:name],[:delete]) + def delete + name = params[:name] + name = "#{name}.#{session[:domain]}" if name.split(".").length <= 1 + + machine = NodeObject.find_node_by_name name + if machine.nil? + flash.now[:notice] = "ERROR: Could not node for name #{name}" + respond_to do |format| + format.html + format.json { render :text => "Host not found", :status => 404 } + end + else + machine.set_state("delete") + respond_to do |format| + format.html { redirect_to :action => :index } + format.json { render :json => {} } + end + end + end + + +end diff --git a/crowbar/change-image/dell/openstack_manager/app/controllers/nodes_controller.rb b/crowbar/change-image/dell/openstack_manager/app/controllers/nodes_controller.rb new file mode 100644 index 00000000000..9f4eda497ff --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/controllers/nodes_controller.rb @@ -0,0 +1,152 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +class NodesController < ApplicationController + + require 'chef' + + # GET /nodes + # GET /nodes.xml + def index + if params.has_key?(:role) + result = NodeObject.all #this is not efficient, please update w/ a search! + @nodes = result.find_all { |node| node.role? params[:role] } + if params.has_key?(:names_only) + names = @nodes.map { |node| node.shortname } + @nodes = {:role=>params[:role], :nodes=>names, :count=>names.count} + end + else + @nodes = NodeObject.all + get_node_and_network(params[:selected]) if params[:selected] + flash[:notice] = "#{t :warning, :scope => :error}: #{t :no_nodes_found, :scope => :error}" if @nodes.empty? #.html_safe if @nodes.empty? + end + respond_to do |format| + format.html # index.html.erb + format.xml { render :xml => @nodes } + format.json { render :json => @nodes } + end + end + + def status + nodes = {} + switches = {} + begin + result = NodeObject.all + result.each do |node| + nodes[node.shortname] = {:status=>node.status, :state=>(I18n.t node.state, :scope => :state)} + count = switches[node.switch_name] || {"ready"=>0, "pending"=>0, "unready"=>0, "unknown"=>0} + count[node.status] += 1 + switches[node.switch_name] = count + end + render :inline => {:nodes=>nodes, :switches=>switches, :count=>nodes.length}.to_json, :cache => false + rescue Exception=>e + count = (e.class.to_s == "Errno::ECONNREFUSED" ? -2 : -1) + Rails.logger.fatal("Failed to iterate over node list due to '#{e.message}'") + render :inline => {:nodes=>nodes, :switches=>switches, :count=>count, :error=>e.message}, :cache => false + end + end + + def hit + action = params[:req] + name = params[:name] || params[:id] + machine = NodeObject.find_node_by_name name + if machine.nil? + render :text=>"Could not find node '#{name}'", :status => 404 + else + case action + when 'reinstall', 'reset', 'update', 'delete' + machine.set_state(action) + when 'reboot' + machine.reboot + when 'shutdown' + machine.shutdown + when 'poweron' + machine.poweron + when 'identify' + machine.identify + when 'allocate' + machine.allocate + else + render :text=>"Invalid hit requeset '#{action}'", :status => 500 + end + end + render :text=>"Attempting '#{action}' for node '#{machine.name}'", :status => 200 + end + + # GET /nodes/1 + # GET /nodes/1.xml + def show + get_node_and_network(params[:id] || params[:name]) + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @node } + format.json { render :json => @node } + end + end + + def edit + get_node_and_network(params[:id] || params[:name]) + end + + def update + if request.post? + get_node_and_network(params[:id] || params[:name]) + if params[:submit] == t('nodes.form.allocate') + @node["crowbar"]["allocated"] = true + save_node + flash[:notice] = t('nodes.form.allocate_node_success') + elsif params[:submit] == t('nodes.form.save') + save_node + flash[:notice] = t('nodes.form.save_node_success') + else + Rails.logger.warn "Unknown action for node edit: #{params[:submit]}" + flash[:notice] = "Unknown action: #{params[:submit]}" + end + else + Rails.logger.warn "PUT is required to update proposal #{params[:id]}" + flash[:notice] = "PUT required" + end + redirect_to nodes_path(:selected => @node.name) + end + + private + + def save_node + @node.usage = [ params[:usage] ] + @node.bios_set = params[:bios] + @node.raid_set = params[:raid] + @node.description = params[:node][:description] unless params[:node].nil? + @node.save + end + + def get_node_and_network(node_name) + @network = {} + notconns = {} + @node = NodeObject.find_node_by_name(node_name) + if @node + # build network information (this may need to move into the object) + interfaces = @node.interface_list.find_all { |n| n=~/^eth/ } + @node[:crowbar][:network].each do |intf, data| + @network[data[:usage]] = {} if @network[data[:usage]].nil? + @network[data[:usage]][intf] = data[:address] || 'n/a' + interfaces.delete(intf) + end + interfaces.map { |i| notconns[i] = nil } + end + @network['[not managed]'] = notconns + @network.sort + end +end diff --git a/crowbar/change-image/dell/openstack_manager/app/controllers/support_controller.rb b/crowbar/change-image/dell/openstack_manager/app/controllers/support_controller.rb new file mode 100644 index 00000000000..6c3230ae70e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/controllers/support_controller.rb @@ -0,0 +1,28 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# + +class SupportController < ApplicationController + def logs + ctime=Time.now.strftime("%Y%m%d-%H%M%S") + system("sudo /opt/dell/bin/gather_logs.sh #{ctime}") + redirect_to "/logs/crowbar-logs-#{ctime}.tar" + end + def get_cli + system("sudo /opt/dell/bin/gather_cli.sh #{request.env['SERVER_ADDR']} #{request.env['SERVER_PORT']}") + redirect_to "/crowbar-cli.tar.gz" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/app/helpers/application_helper.rb b/crowbar/change-image/dell/openstack_manager/app/helpers/application_helper.rb new file mode 100644 index 00000000000..5b4a571a526 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/helpers/application_helper.rb @@ -0,0 +1,59 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# Methods added to this helper will be available to all templates in the application. +module ApplicationHelper + # Is this a Rails 2-ism - csrf_meta_tag? + # app/helpers/application_helper.rb + def csrf_meta_tag + if protect_against_forgery? + out = %(\n) + out << %() + out % [ Rack::Utils.escape_html(request_forgery_protection_token), + Rack::Utils.escape_html(form_authenticity_token) ] + end + end + + def dl_item(term, definition, options={}) + unless definition.blank? && options[:show_if_blank] != true + html = "
#{options[:escape_html] != false ? (h term) : (term)}
" + html += "
#{options[:escape_html] != false ? (h definition) : (definition)}
" + raw html + end + end + + def column_class(current_column, total) + if (current_column % total) == 0 + "first" + elsif (current_column % total) == (total-1) + "last" + end + end + + def hash_to_ul(hash) + result = "
    " + hash.each do |key,value| + result << "
  • #{key}" + if value.is_a?(Hash) + result << hash_to_ul(value) + else + result << ": #{value}
  • " + end + end + result << "
" + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/app/helpers/nodes_helper.rb b/crowbar/change-image/dell/openstack_manager/app/helpers/nodes_helper.rb new file mode 100644 index 00000000000..b8934a0501e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/helpers/nodes_helper.rb @@ -0,0 +1,46 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +module NodesHelper + + def roles_list(roles) + return [] if roles.nil? or roles.empty? + roles.delete_if { |role| role =~ /^.*-config-/ } + (roles.map {|role| "#{role}"} * ', ') #.html_safe + end + + def barclamps_list(roles) + return [] if roles.nil? or roles.empty? + roles.delete_if { |role| !(role =~ /^.*-config-/) } + (roles.map {|role| "#{role.gsub("-config-", " ").titlecase}"} * ', ') #.html_safe + end + + def format_memory(kB) + mem = (kB.to_f / 1024 / 1024) + "#{sprintf("%#1.2f", mem)} GB" + end + + def ip_addresses(ip_list) + html = "" + ip_list.each_pair do |network, addresses| + unless network=='~notconnected' && addresses.nil? + html += "
  • #{network}: #{addresses.keys.collect {|k| "#{k}: #{addresses[k]}"}.join(',')}
  • " + end + end + html + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/app/helpers/proposals_helper.rb b/crowbar/change-image/dell/openstack_manager/app/helpers/proposals_helper.rb new file mode 100644 index 00000000000..d2debf30f6a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/helpers/proposals_helper.rb @@ -0,0 +1,18 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +module ProposalsHelper +end diff --git a/crowbar/change-image/dell/openstack_manager/app/models/chef_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/chef_object.rb new file mode 100644 index 00000000000..3e0a1d49887 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/chef_object.rb @@ -0,0 +1,78 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +class ChefObject + + extend CrowbarOffline + @@CrowbarDomain = nil + + def self.cloud_domain + begin + # NOTE: We are using a global here to avoid lookups. We need to consider some better cache/expiration strategy + if @@CrowbarDomain.nil? + bag = ProposalObject.find_proposal('dns', 'default') + @@CrowbarDomain = bag[:attributes][:dns][:domain] || %x{dnsdomainname}.strip + end + return @@CrowbarDomain + rescue Exception => e + Rails.logger.warn("Could not lookup domain name from Crowbar DNS barclamp attributes/dns/domain key. Error #{e.message}.") + @@CrowbarDomain = nil # reset to make sure we do not cache it + return %x{dnsdomainname}.strip + end + end + + def self.query_chef + begin + chef_init + return Chef::Search::Query.new + rescue + return Chef::Node.new + end + end + + def self.chef_init + Chef::Config.node_name CHEF_NODE_NAME + Chef::Config.client_key CHEF_CLIENT_KEY + Chef::Config.chef_server_url CHEF_SERVER_URL + puts "CHEF_OFFLINE" unless CHEF_ONLINE + end + + def self.chef_escape(str) + str.gsub("-:") { |c| '\\' + c } + end + + def self.crowbar_node(name) + begin + chef_init + return Chef::Node.load(name) + rescue Exception => e + Rails.logger.warn("Could not recover Chef Crowbar Node on load #{name}: #{e.inspect}") + return nil + end + end + + def self.crowbar_data(bag_item) + begin + chef_init + return Chef::DataBag.load "crowbar/#{bag_item}" + rescue Exception => e + Rails.logger.warn("Could not recover Chef Crowbar Data on load #{bag_item}: #{e.inspect}") + return nil + end + end + +end + diff --git a/crowbar/change-image/dell/openstack_manager/app/models/client_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/client_object.rb new file mode 100644 index 00000000000..b5fac4af8b0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/client_object.rb @@ -0,0 +1,46 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# +# Also functions as a data bag item wrapper as well. +# +class ClientObject < ChefObject + + def self.find_client_by_name(name) + begin + chef_init + return ClientObject.new Chef::ApiClient.load(name) + rescue Exception => e + Rails.logger.fatal("Failed to find client: #{name} #{e.message}") + return nil + end + end + + def initialize(client) + @client = client + end + + def save + @client.save + end + + def destroy + @client.destroy + end + +end + + diff --git a/crowbar/change-image/dell/openstack_manager/app/models/crowbar_validator.rb b/crowbar/change-image/dell/openstack_manager/app/models/crowbar_validator.rb new file mode 100644 index 00000000000..d44c11a4096 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/crowbar_validator.rb @@ -0,0 +1,120 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# +# Also functions as a data bag item wrapper as well. +# +#!/usr/bin/ruby + +require 'kwalify' +require 'uri' + +class CrowbarValidator < Kwalify::Validator + + def initialize(schema_filename) + super(Kwalify::Yaml.load_file(schema_filename)) + end + + ## hook method called by Validator#validate() + def validate_hook(value, rule, path, errors) + case rule.name + when 'DiskURL' + begin + arr = URI.split(value) + if arr[0] != "disk" + msg = "Should have protocol disk: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + if arr[2] == "" + msg = "Should have host specified: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + if arr[5] == "" + msg = "Should have path specified: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + rescue URI::InvalidURIError + msg = "Should be a valid URI: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + when 'FQDN' + subregex = /[a-zA-Z0-9_\-]{1,63}/ + endregex = /[\-_]\z/ + startregex = /\A[\-_]/ + numberregex = /\A\d+\z/ + + if value.length > 254 + msg = "Should be a shorter than 254 characters: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + + arr = value.split(".") + if arr.length < 2 + msg = "Must contain 2 or more subdomains: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + if arr.last.length < 2 + msg = "Last subdomain must be more than 1 character: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + arr.each do |dn| + if dn[subregex].nil? + msg = "Can only contain _-A-Za-z0-9: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + if !dn[endregex].nil? + msg = "Last character can not be - or _: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + if !dn[startregex].nil? + msg = "First character can not be - or _: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + if !dn[numberregex].nil? + msg = "Subdomain can not be only numbers: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + end + when 'Email' + regex = /\A([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)\z/ + if value[regex].nil? + msg = "Should be an Email Address: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + when 'DomainName' + regex = /\A((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)\z/ + if value[regex].nil? + msg = "Should be a Domain Name: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + when 'IpAddress' + regex = /\A(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)(?:\.(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)){3}\z/ + if value[regex].nil? + msg = "Should be an IP Address: #{value}" + errors << Kwalify::ValidationError.new(msg, path) + end + when 'IpAddressMap' + regex = /\A(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)(?:\.(?:25[0-5]|(?:2[0-4]|1\d|[1-9])?\d)){3}\z/ + value.each_key do |key| + if key[regex].nil? + msg = "Should be an IP Address: #{key}" + errors << Kwalify::ValidationError.new(msg, path) + end + end + end + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/app/models/node_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/node_object.rb new file mode 100644 index 00000000000..7c2a2e3f284 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/node_object.rb @@ -0,0 +1,417 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +class NodeObject < ChefObject + extend CrowbarOffline + + def self.find(search) + answer = [] + if CHEF_ONLINE + nodes = if search.nil? + ChefObject.query_chef.search "node" + else + ChefObject.query_chef.search "node", "#{chef_escape(search)}" + end + if nodes[2] != 0 + answer = nodes[0].map do |x| + self.dump x, 'node', x.name unless x.nil? + NodeObject.new x + end + answer.delete_if { |x| x.nil? or x.node.nil? } + end + else + files = offline_search 'node-', '' + answer = files.map! { |f| NodeObject.new(recover_json(f)) } + end + return answer + end + + def self.find_all_nodes + self.find nil + end + + def self.find_nodes_by_name(name) + self.find "name:#{chef_escape(name)}" + end + + def self.find_node_by_name(name) + name += ".#{ChefObject.cloud_domain}" unless name =~ /(.*)\.(.)/ + val = if CHEF_ONLINE + ChefObject.crowbar_node(name) + else + self.recover_json(self.nfile('node',name)) + end + return val.nil? ? nil : NodeObject.new(val) + end + def self.all + self.find nil + end + + def self.create_new(new_name, new_domain, new_mac) + machine = Chef::Node.new + machine["crowbar"] = {} + machine["crowbar"]["node-id"] = new_mac + machine["crowbar"]["network"] = {} if machine["crowbar"]["network"].nil? + machine["crowbar"]["decoration"] = { "name" => new_name } + machine["fqdn"] = "#{new_name}.#{new_domain}" + machine.name "#{new_name}.#{new_domain}" + + NodeObject.new machine + end + + def self.human_attribute_name(attrib) + I18n.t attrib, :scope => "model.attributes.node" + end + + def self.status(nodes) + nodes.collect{|c| c.status}.inject(Hash.new(0)){|h,v| h[v] += 1; h} + end + + def initialize(node) + @node = node + end + + def shortname + name.split('.')[0] + end + + def node + @node + end + + def name + @node.nil? ? 'unknown' : @node.name + end + + def status + case state + when "ready" + "ready" + when "discovered" + "pending" + when "discovering", "reset", "delete", "reinstall", "shutdown", "reboot", "poweron", "noupdate" + "unknown" + else + "unready" + end + end + + def ready? + state === 'ready' + end + + def state + return 'unknown' if @node.nil? + if @node['state'] === 'ready' and CHEF_ONLINE + since_last = Time.now.to_i-@node['ohai_time'].to_i + return 'noupdate' if since_last > 1200 # or 20 mins + end + return @node['state'] + end + + def ip + net_info = get_network_by_type("admin") + return net_info["address"] unless net_info.nil? + node["ipaddress"] || (I18n.t :unknown) + end + + def public_ip + net_info = get_network_by_type("public") + return net_info["address"] unless net_info.nil? + node["ipaddress"] || (I18n.t :unknown) + end + + def mac + net_info = get_network_by_type("admin") + if net_info.nil? + (I18n.t :not_set) + else + net_info["mac"] || (I18n.t :unknown) + end + end + + def allocated + @node.nil? ? false : @node["crowbar"]["allocated"] + end + + def allocated=(value) + return false if @node.nil? + @node["crowbar"]["allocated"] = value + end + + def allocated? + @node.nil? ? false : @node["crowbar"]["allocated"] + end + + def name=(value) + return "unknown" if @node.nil? + @node.name value + end + + def memory + node['memory']['total'] rescue nil + end + + def cpu + node['cpu']['0']['model_name'].squeeze(" ").strip rescue nil + end + + def uptime + node["uptime"] + end + + def asset_tag + node["dmi"]["chassis"]["serial_number"] + end + + def number_of_drives + node['crowbar']['disks'].length rescue -1 + end + + def [](attrib) + return nil if @node.nil? + @node[attrib] + end + + def []=(attrib, value) + return nil if @node.nil? + @node[attrib] = value + end + + def run_list(*args) + return nil if @node.nil? + args.length > 0 ? @node.run_list(args) : @node.run_list + end + + def role?(role_name) + return false if @node.nil? + @node.role?(role_name) + end + + def roles + @node['roles'].nil? ? nil : @node['roles'].sort + end + + def save + if @node["crowbar-revision"].nil? + @node["crowbar-revision"] = 0 + Rails.logger.debug("Starting Node Revisions: #{@node.name} - unset") + else + Rails.logger.debug("Starting Node Revisions: #{@node.name} - #{@node["crowbar-revision"]}") + @node["crowbar-revision"] = @node["crowbar-revision"] + 1 + end + Rails.logger.debug("Saving node: #{@node.name} - #{@node["crowbar-revision"]}") + if CHEF_ONLINE + @node.save + else + NodeObject.offline_cache(@node, NodeObject.nfile('node', @node.name)) + end + Rails.logger.debug("Done saving node: #{@node.name} - #{@node["crowbar-revision"]}") + end + + def destroy + Rails.logger.debug("Destroying node: #{@node.name} - #{@node["crowbar-revision"]}") + @node.destroy + Rails.logger.debug("Done with removal of node: #{@node.name} - #{@node["crowbar-revision"]}") + end + + def get_network_by_type(type) + return nil if @node.nil? + @node["crowbar"]["network"].each do |intf, data| + return data if data["usage"] == type + end + nil + end + + def get_network_by_interface(intf) + return nil if @node.nil? + @node["crowbar"]["network"][intf] + end + + def admin? + return false if @node.nil? + @node["crowbar"]["admin_node"] + end + + def interface_list + return [] if @node.nil? + answer = [] + @node["network"]["interfaces"].each do |k,v| + next if k == "lo" # no loopback, please + next if k =~ /^sit/ # Ignore sit interfaces + next if k =~ /\.\d+/ # no vlan interfaces, please + answer << k + end + answer + end + def switch_name + unless node["crowbar"].nil? or node["crowbar"]["switch_config"].nil? + switch_name = node["crowbar"]["switch_config"]["eth0"]["switch_name"] || (I18n.t :undetermined) + switch_name = (I18n.t :undetermined) if switch_name == -1 + switch_name.to_s.gsub(':', '-') + else + switch_name = (I18n.t :undetermined) + end + end + + def switch_port + unless node["crowbar"].nil? or node["crowbar"]["switch_config"].nil? + switch_name = node["crowbar"]["switch_config"]["eth0"]["switch_port"] || (I18n.t :undetermined) + else + switch_name = (I18n.t :undetermined) + end + end + + def location + unless node["crowbar"].nil? or node["crowbar"]["switch_config"].nil? + location = node["crowbar"]["switch_config"]["eth0"]["switch_port"] || (I18n.t :not_set) + else + location = (I18n.t :not_set) + end + end + + def description + unless node["crowbar"].nil? or node["crowbar"]["description"].nil? + node["crowbar"]["description"] || (I18n.t :not_set) + else + I18n.t :not_set + end + end + + def description=(value) + node["crowbar"]["description"] = value + end + + def hardware + node["dmi"].system.product_name + end + + def usage + return [] if @node.nil? + return [] if @node["crowbar"].nil? + @node["crowbar"]["usage"] + end + + def usage=(value) + return [] if @node.nil? + return [] if @node["crowbar"].nil? + @node["crowbar"]["usage"] = value + end + + def raid_set + return nil if @node.nil? + return nil if @node["crowbar"].nil? + return nil if @node["crowbar"]["hardware"].nil? + @node["crowbar"]["hardware"]["raid_set"] + end + + def raid_set=(value) + return nil if @node.nil? + return nil if @node["crowbar"].nil? + @node["crowbar"]["hardware"] = {} if @node["crowbar"]["hardware"].nil? + @node["crowbar"]["hardware"]["raid_set"] = value + end + + def bios_set + return nil if @node.nil? + return nil if @node["crowbar"].nil? + return nil if @node["crowbar"]["hardware"].nil? + @node["crowbar"]["hardware"]["bios_set"] + end + + def bios_set=(value) + return nil if @node.nil? + return nil if @node["crowbar"].nil? + @node["crowbar"]["hardware"] = {} if @node["crowbar"]["hardware"].nil? + @node["crowbar"]["hardware"]["bios_set"] = value + end + + def to_hash + return {} if @node.nil? + @node.to_hash + end + + def set_state(state) + if CHEF_ONLINE + # use the real transition function for this + cb = CrowbarService.new Rails.logger + results = cb.transition "default", @node.name, state + else + puts "Node #{name} Chef State Changed to #{state}" + @node['state'] = state + save + end + + if state == "reset" or state == "reinstall" or state == "update" + if CHEF_ONLINE + bmc = get_network_by_type("bmc") + system("ipmitool -H #{bmc["address"]} -U crowbar -P crowbar power cycle") unless bmc.nil? + else + NodeObject.clear_cache @node + puts "Node #{name} to #{state} caused cache object to be deleted." + end + end + results + end + + def reboot + set_state("reboot") + bmc = get_network_by_type("bmc") + return puts "Node #{name} IMPI Reboot call to #{bmc["address"]}" unless CHEF_ONLINE + system("ipmitool -H #{bmc["address"]} -U crowbar -P crowbar power cycle") unless bmc.nil? + end + + def shutdown + set_state("shutdown") + bmc = get_network_by_type("bmc") + bmc = get_network_by_type("bmc") + if CHEF_ONLINE + system("ipmitool -H #{bmc["address"]} -U crowbar -P crowbar power off") unless bmc.nil? + else + return puts "Node #{name} IMPI Shutdown call to #{bmc["address"]}" + end + end + + def poweron + set_state("poweron") + bmc = get_network_by_type("bmc") + if CHEF_ONLINE + system("ipmitool -H #{bmc["address"]} -U crowbar -P crowbar power on") unless bmc.nil? + else + puts "Node #{name} IMPI Power On call to #{bmc["address"]}" + end + end + + def power_change_state(state) + @node['state'] = state + @node.save + end + + def identify + bmc = get_network_by_type("bmc") + return puts "Node #{name} IMPI Identify call to #{bmc["address"]}" unless CHEF_ONLINE + system("ipmitool -H #{bmc["address"]} -U crowbar -P crowbar chassis identify") unless bmc.nil? + end + + def allocate + return if @node.nil? + self.allocated = true + @node.save + end + +end + + diff --git a/crowbar/change-image/dell/openstack_manager/app/models/proposal_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/proposal_object.rb new file mode 100644 index 00000000000..6178e202499 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/proposal_object.rb @@ -0,0 +1,181 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# +# Also functions as a data bag item wrapper as well. +# +class ProposalObject < ChefObject + + extend CrowbarOffline + BC_PREFIX = 'bc-template-' + + def self.find_data_bag_item(bag) + begin + chef_init #elimiate + bag = ProposalObject.new(Chef::DataBag.load bag) #should use new syntax + return bag + rescue + return nil + end + end + + def self.find(search) + props = [] + begin + if CHEF_ONLINE + arr = ChefObject.query_chef.search("crowbar", "id:#{chef_escape(search)}") + if arr[2] != 0 + props = arr[0].map do |x| + self.dump x, 'data_bag_item_crowbar-bc', x.name[/bc-(.*)/,1] unless x.nil? + ProposalObject.new x + end + props.delete_if { |x| x.nil? or x.item.nil? } + end + else + files = self.offline_search('data_bag_item_crowbar-', search.chop) + props = files.map { |file| ProposalObject.new(self.recover_json(file)) } + end + rescue Exception => e + Rails.logger.error("Could not recover Chef Crowbar data searching for '#{search}' due to '#{e.inspect}'") + end + return props + end + + def self.all + self.find 'bc-*' + end + + def self.find_proposals(barclamp) + self.find "bc-#{barclamp}-*" + end + + def self.find_proposal(barclamp, name) + self.find_proposal_by_id "bc-#{barclamp}-#{name}" + end + + def self.find_proposal_by_id(id) + val = if CHEF_ONLINE + ChefObject.crowbar_data(id) + else + self.recover_json(self.nfile('data_bag_item_crowbar-bc', id[/bc-(.*)/,1])) + end + return val.nil? ? nil : ProposalObject.new(val) + end + + def self.human_attribute_name(attrib) + I18n.t attrib, :scope => "model.attributes.proposal" + end + + def item + @item + end + + def id + @item['id'] + end + + def name + @item.name[/crowbar_bc-(.*)-(.*)$/,2] + end + + def barclamp + @item.name[/crowbar_bc-(.*)-(.*)$/,1] + end + + def status + bc = @item["deployment"][self.barclamp] + if bc.nil? + "unready" + else + return "unready" if bc.has_key? "crowbar-committing" and bc["crowbar-committing"] + return "pending" if bc.has_key? "crowbar-queued" and bc["crowbar-queued"] + "ready" + end + end + + def description + @item['description'] + end + + def elements + @item.raw_data['deployment'][self.barclamp]["elements"] + end + + def all_elements + @item.raw_data['deployment'][self.barclamp]["element_order"].flatten.uniq + end + + def active? + inst = "#{barclamp}-config-#{name}" + role = RoleObject.find_role_by_name(inst) + return role.nil? + end + + def raw_data + @item.raw_data + end + + def raw_data=(value) + @item.raw_data = value + end + + def [](attrib) + @item[attrib] + end + + def []=(attrib, value) + @item[attrib] = value + end + + def initialize(x) + @item = x + end + + def save + @item["deployment"] = {} if @item["deployment"].nil? + @item["deployment"][barclamp] = {} if @item["deployment"][barclamp].nil? + if @item["deployment"][barclamp]["crowbar-revision"].nil? + @item["deployment"][barclamp]["crowbar-revision"] = 0 + else + @item["deployment"][barclamp]["crowbar-revision"] = @item["deployment"][barclamp]["crowbar-revision"] + 1 + end + Rails.logger.debug("Saving data bag item: #{@item["id"]} - #{@item["deployment"][barclamp]["crowbar-revision"]}") + if CHEF_ONLINE + @item.save + else + ProposalObject.offline_cache(@item, ProposalObject.nfile('data_bag_item_crowbar', @item.id)) + end + Rails.logger.debug("Done saving data bag item: #{@item["id"]} - #{@item["deployment"][barclamp]["crowbar-revision"]}") + end + + def destroy + Rails.logger.debug("Destroying data bag item: #{@item["id"]} - #{@item["deployment"][barclamp]["crowbar-revision"]}") + @item.destroy(@item.data_bag, @item["id"]) + Rails.logger.debug("Done removal of data bag item: #{@item["id"]} - #{@item["deployment"][barclamp]["crowbar-revision"]}") + end + + private + + # 'array' is the unsorted set of objects + # 'att_sym' is the symbol of the attribute each object in array, that is represented in index_array + # 'index_array' is the ordered array of values + def sort_with_index(array, att_sym, index_array) + return array.sort do |a, b| + index_array.index(a.send(att_sym)).to_i <=> index_array.index(b.send(att_sym)).to_i + end + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/app/models/remote_service_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/remote_service_object.rb new file mode 100644 index 00000000000..d82dad38f97 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/remote_service_object.rb @@ -0,0 +1,56 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# +# Also functions as a data bag item wrapper as well. +# +class RemoteServiceObject + require 'patron' + + def self.build_session(timeout) + sess = Patron::Session.new + sess.timeout = timeout + sess.headers['Accept'] = "application/json" + sess.headers['Content-Type'] = "application/json" + sess + end + + def self.common_return(res) + return [res.body, res.status.to_i ] if res.status.to_i != 200 + struct = JSON.parse(res.body) + [struct, 200] + end + + # Helper routines to contact the service in question. + def self.post_json(path, data, timeout = 120) + sess = build_session timeout + res = sess.post(path, data) + common_return res + end + + def self.put_json(path, data, timeout = 120) + sess = build_session timeout + res = sess.put(path, data) + common_return res + end + + def self.get_json(path, timeout = 120) + sess = build_session timeout + res = sess.get(path) + common_return res + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/app/models/role_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/role_object.rb new file mode 100644 index 00000000000..7220f0d61c2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/role_object.rb @@ -0,0 +1,153 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +class RoleObject < ChefObject + + extend CrowbarOffline + + def self.all + self.find_roles_by_search(nil) + end + + def self.find_roles_by_name(name) + roles = [] + if CHEF_ONLINE + #TODO this call could be moved to fild_roles_by_search + arr = ChefObject.query_chef.search "role", "name:#{chef_escape(name)}" + if arr[2] != 0 + roles = arr[0].map { |x| RoleObject.new x } + roles.delete_if { |x| x.nil? or x.role.nil? } + end + else + roles = find_roles_by_search name + end + roles + end + + def self.find_roles_by_search(search) + roles = [] + if CHEF_ONLINE + arr = if search.nil? + ChefObject.query_chef.search "role" + else + ChefObject.query_chef.search "role", search + end + if arr[2] != 0 + roles = arr[0].map { |x| RoleObject.new x } + roles.delete_if { |x| x.nil? or x.role.nil? } + arr[0].each { |role| self.dump role, 'role', role.name unless role.nil? } + end + else + files = offline_search 'role-', search + roles = files.map! { |f| RoleObject.new(recover_json(f)) } + end + roles + end + + def self.find_role_by_name(name) + if CHEF_ONLINE + begin + chef_init + return RoleObject.new Chef::Role.load(name) + rescue + return nil + end + else + answer = self.recover_json(self.nfile('role',name)) + return answer.nil? ? nil : RoleObject.new(answer) + end + end + + def self.human_attribute_name(attrib) + I18n.t attrib, :scope => "model.attributes.role" + end + + def barclamp + @role.name.split("-")[0] + end + + def inst + @role.name.gsub("#{self.barclamp}-config-", "") + end + + def role + @role + end + + def name + @role.name + end + + def name=(value) + @role.name value + end + + def description + @role.description + end + + def description=(value) + @role.description value + end + + def default_attributes + @role.default_attributes + end + + def default_attributes=(value) + @role.default_attributes value + end + + def override_attributes + @role.override_attributes + end + + def override_attributes=(value) + @role.override_attributes value + end + + def initialize(x) + @role = x + end + + def save + @role.override_attributes[barclamp] = {} if @role.override_attributes[barclamp].nil? + if @role.override_attributes[barclamp]["crowbar-revision"].nil? + @role.override_attributes[barclamp]["crowbar-revision"] = 0 + else + @role.override_attributes[barclamp]["crowbar-revision"] = @role.override_attributes[barclamp]["crowbar-revision"] + 1 + end + Rails.logger.debug("Saving role: #{@role.name} - #{@role.override_attributes[barclamp]["crowbar-revision"]}") + if CHEF_ONLINE + @role.save + else + RoleObject.offline_cache(@role, RoleObject.nfile('role', @role.name)) + end + Rails.logger.debug("Done saving role: #{@role.name} - #{@role.override_attributes[barclamp]["crowbar-revision"]}") + end + + def destroy + Rails.logger.debug("Destroying role: #{@role.name} - #{@role.override_attributes[barclamp]["crowbar-revision"]}") + @role.destroy + Rails.logger.debug("Done removing role: #{@role.name} - #{@role.override_attributes[barclamp]["crowbar-revision"]}") + end + + def elements + @role.override_attributes[self.barclamp]["elements"] + end + +end + diff --git a/crowbar/change-image/dell/openstack_manager/app/models/service_object.rb b/crowbar/change-image/dell/openstack_manager/app/models/service_object.rb new file mode 100644 index 00000000000..b7c974d471c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/models/service_object.rb @@ -0,0 +1,651 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# +# Also functions as a data bag item wrapper as well. +# +require 'chef' +require 'json' + +class ServiceObject + + extend CrowbarOffline + + def self.all + bc = {} + ProposalObject.find("#{ProposalObject::BC_PREFIX}*").each do |bag| + bc[bag.item.name[/#{ProposalObject::BC_PREFIX}(.*)/,1]] = bag.item[:description] + end + bc.delete_if { |k, v| bc.has_key? k[/^(.*)-(.*)/,0] } + return bc + end + + + def acquire_lock(name) + @logger.debug("Acquire #{name} lock enter") + f = File.new("tmp/#{name}.lock", File::RDWR|File::CREAT, 0644) + @logger.debug("Acquiring #{name} lock") + rc = false + count = 0 + while rc == false do + count = count + 1 + @logger.debug("Attempt #{name} Lock: #{count}") + rc = f.flock(File::LOCK_EX|File::LOCK_NB) + sleep 1 if rc == false + end + @logger.debug("Acquire #{name} lock exit: #{f.inspect}, #{rc}") + f + end + + def release_lock(f) + @logger.debug("Release lock enter: #{f.inspect}") + f.flock(File::LOCK_UN) + f.close + @logger.debug("Release lock exit") + end + + def queue_proposal(inst, bc = @bc_name) + begin + f = acquire_lock "queue" + + db = ProposalObject.find_data_bag_item "crowbar/queue" + if db.nil? + new_queue = Chef::DataBagItem.new + new_queue.data_bag "crowbar" + new_queue["id"] = "queue" + new_queue["proposal_queue"] = [] + db = ProposalObject.new new_queue + end + + db["proposal_queue"].each do |item| + return if item["barclamp"] == bc and item["inst"] == inst + end + + db["proposal_queue"] << { "barclamp" => bc, "inst" => inst } + db.save + rescue Exception => e + @logger.error("Error queuing proposal for #{bc}:#{inst}: #{e.message}") + ensure + release_lock f + end + + prop = ProposalObject.find_proposal(bc, inst) + prop["deployment"][bc]["crowbar-queued"] = true + prop.save + end + + def dequeue_proposal(inst, bc = @bc_name) + begin + f = acquire_lock "queue" + + db = ProposalObject.find_data_bag_item "crowbar/queue" + return if db.nil? + + db["proposal_queue"].delete_if { |item| item["barclamp"] == bc and item["inst"] == inst } + db.save + prop = ProposalObject.find_proposal(bc, inst) + unless prop.nil? + prop["deployment"][bc]["crowbar-queued"] = false + prop.save + + remove_pending_elements(bc, inst, prop["deployment"][bc]["elements"]) + end + rescue Exception => e + @logger.error("Error dequeuing proposal for #{bc}:#{inst}: #{e.message}") + return false + ensure + release_lock f + end + true + end + + def process_queue + list = [] + queue = [] + begin + f = acquire_lock "queue" + + db = ProposalObject.find_data_bag_item "crowbar/queue" + return if db.nil? + + queue = db["proposal_queue"] + rescue Exception => e + @logger.error("Error queuing proposal for #{bc}:#{inst}: #{e.message}") + ensure + release_lock f + end + + # Test for ready + queue.each do |item| + prop = ProposalObject.find_proposal(item["barclamp"], item["inst"]) + dequeue_proposal(item["inst"], item["barclamp"]) if prop.nil? + next if prop.nil? + list << item if elements_not_ready(prop["deployment"][item["barclamp"]]["elements"]).empty? + end + + # For each ready item, apply it. + list.each do |item| + bc = item["barclamp"] + inst = item["inst"] + service = eval("#{bc.capitalize}Service.new @logger") + answer = service.proposal_commit(inst) + dequeue_proposal(inst, bc) if answer[0] == 200 + end + end + + def elements_not_ready(elements) + # Get all the nodes + all_new_nodes = [] + elements.each do |elem, nodes| + all_new_nodes << nodes + end + all_new_nodes.flatten! + + # Check to see if we should delay our commit until nodes are ready. + delay = [] + all_new_nodes.each do |n| + node = NodeObject.find_node_by_name(n) + next if node.nil? + + # if a node is not allocated, mark it as allocated + unless node.allocated? + node.allocated = true + node.save + end + + delay << n if node["state"] != "ready" + end + delay + end + + def add_pending_elements(bc, inst, elements) + # Create map with nodes and their element list + all_new_nodes = {} + elements.each do |elem, nodes| + nodes.each do |node| + all_new_nodes[node] = [] if all_new_nodes[node].nil? + all_new_nodes[node] << elem + end + end + + # Add the entries to the nodes. + all_new_nodes.each do |n, val| + node = NodeObject.find_node_by_name(n) + next if node.nil? + + node["crowbar"]["pending"] = {} if node["crowbar"]["pending"].nil? + node["crowbar"]["pending"]["#{bc}-#{inst}"] = val + node.save + end + end + + def remove_pending_elements(bc, inst, elements) + # Create map with nodes and their element list + all_new_nodes = {} + elements.each do |elem, nodes| + nodes.each do |node| + all_new_nodes[node] = [] if all_new_nodes[node].nil? + all_new_nodes[node] << elem + end + end + + # Remove the entries from the nodes. + all_new_nodes.each do |n| + node = NodeObject.find_node_by_name(n) + next if node.nil? + unless node["crowbar"]["pending"].nil? or node["crowbar"]["pending"]["#{bc}-#{inst}"].nil? + node["crowbar"]["pending"]["#{bc}-#{inst}"] = {} + node.save + end + end + end + + def bc_name=(new_name) + @bc_name = new_name + end + + def bc_name + @bc_name + end + + def initialize(thelogger) + @bc_name = "unknown" + @logger = thelogger + end + + def versions + [200, { :versions => [ "1.0" ] }] + end + + def transition + [200, {}] + end + + def list_active + roles = RoleObject.find_roles_by_name("#{@bc_name}-config-*") + roles.map! { |r| r.name.gsub("#{@bc_name}-config-","") } unless roles.empty? + [200, roles] + end + + def show_active(inst) + inst = "#{@bc_name}-config-#{inst}" + + role = RoleObject.find_role_by_name(inst) + + if role.nil? + [404, "Active instance not found"] + else + [200, role] + end + end + + def clean_proposal(proposal) + proposal.delete("controller") + proposal.delete("action") + proposal.delete("barclamp") + proposal.delete("name") + proposal.delete("_method") + proposal.delete("authenticity_token") + end + + # + # Proposal is a json structure (not a ProposalObject) + # Use to create or update an active instance + # + def active_update(proposal, inst) + begin + role = ServiceObject.proposal_to_role(proposal, @bc_name) + clean_proposal(proposal) + validate_proposal proposal + apply_role(role, inst) + rescue Net::HTTPServerException => e + [e.response.code, {}] + rescue Chef::Exceptions::ValidationFailed => e2 + [400, e2.message] + end + end + + def destroy_active(inst) + inst = "#{@bc_name}-config-#{inst}" + role = RoleObject.find_role_by_name(inst) + if role.nil? + [404, {}] + else + # By nulling the elements, it functions as a remove + dep = role.override_attributes + dep[@bc_name]["elements"] = {} + role.override_attributes = dep + answer = apply_role(role, inst) + role.destroy + answer + end + end + + def elements + roles = RoleObject.find_roles_by_name("#{@bc_name}-*") + cull_roles = RoleObject.find_roles_by_name("#{@bc_name}-config-*") + roles.delete_if { |r| cull_roles.include?(r) } unless roles.empty? + roles.map! { |r| r.name } unless roles.empty? + [200, roles] + end + + def element_info + nodes = NodeObject.find_all_nodes + nodes.map! { |n| n.name } unless nodes.empty? + [200, nodes] + end + + def proposals_raw + ProposalObject.find_proposals(@bc_name) + end + + def proposals + props = proposals_raw + props.map! { |p| p["id"].gsub("bc-#{@bc_name}-", "") } unless props.empty? + [200, props] + end + + def proposal_show(inst) + prop = ProposalObject.find_proposal(@bc_name, inst) + if prop.nil? + [404, {}] + else + [200, prop] + end + end + + # + # This can be overridden to provide a better creation proposal + # + def create_proposal + prop = ProposalObject.find_proposal("template", @bc_name) + prop.raw_data + end + + def proposal_create(params) + base_id = params["id"] + params["id"] = "bc-#{@bc_name}-#{params["id"]}" + + prop = ProposalObject.find_proposal(@bc_name, base_id) + return [400, I18n.t('.name_exists', :scope=>'model.service')] unless prop.nil? + return [400, I18n.t('.too_short', :scope=>'model.service')] if base_id.length == 0 + return [400, I18n.t('.illegal_chars', :scope=>'model.service')] if base_id =~ /[^A-Za-z0-9_]/ + + base = create_proposal + base["deployment"][@bc_name]["config"]["environment"] = "#{@bc_name}-config-#{base_id}" + proposal = base.merge(params) + clean_proposal(proposal) + _proposal_update proposal + end + + def proposal_edit(params) + params["id"] = "bc-#{@bc_name}-#{params["id"]}" + proposal = {}.merge(params) + clean_proposal(proposal) + _proposal_update proposal + end + + def proposal_delete(inst) + prop = ProposalObject.find_proposal(@bc_name, inst) + if prop.nil? + [404, {}] + else + prop.destroy + [200, {}] + end + end + + def proposal_commit(inst) + prop = ProposalObject.find_proposal(@bc_name, inst) + + if prop.nil? + [404, "#{I18n.t('.cannot_find', :scope=>'model.service')}: #{@bc_name}.#{inst}"] + elsif prop["deployment"][@bc_name]["crowbar-committing"] + [402, "#{I18n.t('.already_commit', :scope=>'model.service')}: #{@bc_name}.#{inst}"] + else + # Put mark on the wall + prop["deployment"][@bc_name]["crowbar-committing"] = true + prop.save + + answer = active_update prop.raw_data, inst + + # Unmark the wall + prop = ProposalObject.find_proposal(@bc_name, inst) + prop["deployment"][@bc_name]["crowbar-committing"] = false + prop.save + + answer + end + end + + # + # This can be overridden to get better validation if needed. + # + def validate_proposal proposal + path = "/opt/dell/chef/data_bags/crowbar" + path = "schema" unless CHEF_ONLINE + validator = CrowbarValidator.new("#{path}/bc-template-#{@bc_name}.schema") + + errors = validator.validate(proposal) + if errors && !errors.empty? + strerrors = "" + errors.each do |e| + strerrors += "#{e.message}\n" + end + raise Chef::Exceptions::ValidationFailed.new(strerrors) + end + end + + def _proposal_update(proposal) + data_bag_item = Chef::DataBagItem.new + + begin + data_bag_item.raw_data = proposal + data_bag_item.data_bag "crowbar" + + validate_proposal proposal + + prop = ProposalObject.new data_bag_item + prop.save + + [200, {}] + rescue Net::HTTPServerException => e + [e.response.code, {}] + rescue Chef::Exceptions::ValidationFailed => e2 + [400, e2.message] + end + end + + # + # This is a role output function + # Can take either a RoleObject or a Role. + # + def self.role_to_proposal(role, bc_name) + proposal = {} + + proposal["id"] = role.name.gsub("#{bc_name}-config-", "bc-#{bc_name}-") + proposal["description"] = role.description + proposal["attributes"] = role.default_attributes + proposal["deployment"] = role.override_attributes + + proposal + end + + # + # From a proposal json + # + def self.proposal_to_role(proposal, bc_name) + role = Chef::Role.new + role.name proposal["id"].gsub("bc-#{bc_name}-", "#{bc_name}-config-") + role.description proposal["description"] + role.default_attributes proposal["attributes"] + role.override_attributes proposal["deployment"] + RoleObject.new role + end + + # + # After validation, this is where the role is applied to the system + # The old instance (if one exists) is compared with the new instance. + # roles are removed and delete roles are added (if they exist) for nodes leaving roles + # roles are added for nodes joining roles. + # Calls chef-client on nodes + # + # This function can be overriden to define a barclamp specific operation. + # A call is provided that receives the role and all string names of the nodes before the chef-client call + # + def apply_role(role, inst) + # Query for this role + old_role = RoleObject.find_role_by_name(role.name) + + nodes = {} + + # Get the new elements list + new_deployment = role.override_attributes[@bc_name] + new_elements = new_deployment["elements"] + element_order = new_deployment["element_order"] + + delay = elements_not_ready(new_elements) + unless delay.empty? + add_pending_elements(@bc_name, inst, new_elements) + queue_proposal(inst) + return [202, delay] + end + + # make sure the role is saved + role.save + + # Build a list of old elements + old_elements = {} + old_deployment = old_role.override_attributes[@bc_name] unless old_role.nil? + old_elements = old_deployment["elements"] unless old_deployment.nil? + element_order = old_deployment["element_order"] if (!old_deployment.nil? and element_order.nil?) + + # Merge the parts based upon the element install list. + all_nodes = [] + run_order = [] + element_order.each do | elems | + r_nodes = [] + elems.each do |elem| + old_nodes = old_elements[elem] + new_nodes = new_elements[elem] + + unless old_nodes.nil? + elem_remove = nil + tmprole = RoleObject.find_role_by_name "#{elem}_remove" + unless tmprole.nil? + elem_remove = tmprole.name + end + + old_nodes.each do |n| + if new_nodes.nil? or !new_nodes.include?(n) + nodes[n] = { :remove => [], :add => [] } if nodes[n].nil? + nodes[n][:remove] << elem + nodes[n][:add] << elem_remove unless elem_remove.nil? + r_nodes << n + end + end + end + + unless new_nodes.nil? + new_nodes.each do |n| + all_nodes << n + if old_nodes.nil? or !old_nodes.include?(n) + nodes[n] = { :remove => [], :add => [] } if nodes[n].nil? + nodes[n][:add] << elem + end + r_nodes << n unless r_nodes.include?(n) + end + end + end + run_order << r_nodes unless r_nodes.empty? + end + + # Clean the run_lists + admin_nodes = [] + nodes.each do |n, lists| + node = NodeObject.find_node_by_name(n) + next if node.nil? + + admin_nodes << n if node.admin? + + save_it = false + + rlist = lists[:remove] + alist = lists[:add] + + # Remove the roles being lost + rlist.each do |item| + next unless node.role? item + node.run_list.run_list_items.delete "role[#{item}]" + save_it = true + end + + # Add the roles being gained + alist.each do |item| + next if node.role? item + node.run_list.run_list_items << "role[#{item}]" + save_it = true + end + + # Make sure the config role is on the nodes in this barclamp, otherwise remove it + if all_nodes.include?(node.name) + # Add the config role + unless node.role?(role.name) + node.run_list.run_list_items << "role[#{role.name}]" + save_it = true + end + else + # Remove the config role + if node.role?(role.name) + node.run_list.run_list_items.delete "role[#{role.name}]" + save_it = true + end + end + + node.save if save_it + end + + apply_role_pre_chef_call(old_role, role, all_nodes) + + # Each batch is a list of nodes that can be done in parallel. + ran_admin = false + run_order.each do | batch | + next if batch.empty? + snodes = "" + admin_list = "" + batch.each do |n| + # Run admin nodes a different way. + if admin_nodes.include?(n) + admin_list = admin_list + " OR " if admin_list != "" + admin_list = admin_list + "name:#{n}" + ran_admin = true + next + end + snodes = snodes + " OR " if snodes != "" + snodes = snodes + "name:#{n}" + end + + # Only take the actions if we are online + if CHEF_ONLINE + system("sudo -i -u root \"knife ssh '#{snodes}' chef-client\"") + system("sudo -i -u root \"knife ssh '#{snodes}' chef-client\"") + + system("sudo -i -u root \"knife ssh '#{admin_list}' /opt/dell/bin/single_chef_client.sh\"") if admin_list != "" + system("sudo -i -u root \"knife ssh '#{admin_list}' /opt/dell/bin/single_chef_client.sh\"") if admin_list != "" + end + end + + system("sudo /opt/dell/bin/single_chef_client.sh") if CHEF_ONLINE and !ran_admin + [200, {}] + end + + def apply_role_pre_chef_call(old_role, role, all_nodes) + # noop by default. + end + + def add_role_to_instance_and_node(barclamp, instance, name, prop, role, newrole) + node = NodeObject.find_node_by_name name + return false if node.nil? + + prop["deployment"][barclamp]["elements"][newrole] = [] if prop["deployment"][barclamp]["elements"][newrole].nil? + unless prop["deployment"][barclamp]["elements"][newrole].include?(node.name) + prop["deployment"][barclamp]["elements"][newrole] << node.name + prop.save + end + + role.override_attributes[barclamp]["elements"][newrole] = [] if role.override_attributes[barclamp]["elements"][newrole].nil? + unless role.override_attributes[barclamp]["elements"][newrole].include?(node.name) + role.override_attributes[barclamp]["elements"][newrole] << node.name + role.save + end + + save_it = false + unless node.role?(newrole) + node.run_list.run_list_items << "role[#{newrole}]" + save_it = true + end + + unless node.role?("#{barclamp}-config-#{instance}") + node.run_list.run_list_items << "role[#{barclamp}-config-#{instance}]" + save_it = true + end + + node.save if save_it + true + end + +end + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_attribute_helper.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_attribute_helper.html.haml new file mode 100644 index 00000000000..8a37229fb82 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_attribute_helper.html.haml @@ -0,0 +1,27 @@ + +:javascript + function update_value(path, part, type) { + var input = document.getElementById('proposal_attributes'); + var json = JSON.parse(input.value); + + var comp = document.getElementById(part); + var value = comp.value; + + // Do type conversions + if (type == 'boolean') { + value = (value == 'true' || value == 'True') + } + else if (type == 'integer') { + value = parseInt(value) + } + + // Start the path processing + var path_parts = path.split('/'); + var data = json; + while (path_parts.length > 1) + data = data[path_parts.shift()]; + data[path_parts.shift()] = value; + + input.value = JSON.stringify(json); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_crowbar_edit_attributes.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_crowbar_edit_attributes.html.haml new file mode 100644 index 00000000000..4540427de6d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_crowbar_edit_attributes.html.haml @@ -0,0 +1,7 @@ + +%input#proposal_attributes{:type => "hidden", :name => "proposal_attributes", :value => @proposal.raw_data['attributes'][@proposal.barclamp].to_json} +%p + %label{:for => "proposal_attributes"}= t('.attributes') + = link_to t('raw'), proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => @dep_raw, :attr_raw => true), :style => "float: right;" + %div.container + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_crowbar_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_crowbar_edit_deployment.html.haml new file mode 100644 index 00000000000..80246566cb0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_crowbar_edit_deployment.html.haml @@ -0,0 +1,7 @@ + + +%input#proposal_development{:type => "hidden", :name => "proposal_deployment", :value => @proposal.raw_data['deployment'][@proposal.barclamp].to_json } +%p + %label{:for => "drag_drop"}= t('.deployment') + = link_to "Raw", proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => true, :attr_raw => @attr_raw), :style => "float: right;" + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_deployer_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_deployer_edit_deployment.html.haml new file mode 100644 index 00000000000..8380f465d1d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_deployer_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "deployer-client": { "unique": false, "count": -1 } }; + node_selector($('#drag_drop'), constraints); + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_dns_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_dns_edit_deployment.html.haml new file mode 100644 index 00000000000..e52cd2e67ec --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_dns_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "dns-server": { "unique": false, "count": 1 }, "dns-client": { "unique": false, "count": -1 } }; + node_selector($('#drag_drop'), constraints); + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_edit_attributes_raw.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_edit_attributes_raw.html.haml new file mode 100644 index 00000000000..934ace1c001 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_edit_attributes_raw.html.haml @@ -0,0 +1,9 @@ +%p + %label{:for => "proposal_attributes"} #{t('.title')} + = link_to t('custom'), proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => @dep_raw, :attr_raw => false), :style => "float: right;" += find_and_preserve do + %textarea#proposal_attributes.editor{:name => "proposal_attributes"} + -# TODO: don't do this anymore (currently working around a bug in the JSON gem) + -# = JSON.pretty_generate @proposal.raw_data['attributes'][@proposal.barclamp].to_hash + = JSON.pretty_generate JSON.parse(@proposal.raw_data['attributes'][@proposal.barclamp].to_json) + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_edit_deployment_raw.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_edit_deployment_raw.html.haml new file mode 100644 index 00000000000..1ef158f6900 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_edit_deployment_raw.html.haml @@ -0,0 +1,7 @@ +%p + %label{:for => "proposal_deployment"} #{t('.title')} + = link_to t('custom'), proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => false, :attr_raw => @attr_raw), :style => "float: right;" += find_and_preserve do + %textarea#proposal_deployment.editor{:name => "proposal_deployment"} + = JSON.pretty_generate JSON.parse(@proposal.raw_data['deployment'][@proposal.barclamp].to_json) + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ganglia_edit_attributes.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ganglia_edit_attributes.html.haml new file mode 100644 index 00000000000..1ee57586746 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ganglia_edit_attributes.html.haml @@ -0,0 +1,7 @@ + +%input#proposal_attributes{:type => "hidden", :name => "proposal_attributes", :value => @proposal.raw_data['attributes'][@proposal.barclamp].to_json} +%p + %label{:for => "proposal_attributes"}= t('.attributes') + = link_to "Raw", proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => @dep_raw, :attr_raw => true), :style => "float: right;" + %div.container + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ganglia_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ganglia_edit_deployment.html.haml new file mode 100644 index 00000000000..7b8a9a939c4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ganglia_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "ganglia-server": { "unique": true, "count": 1 }, "ganglia-client": { "unique": true, "count": -1 } } + node_selector($('#drag_drop'), constraints) + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ipmi_edit_attributes.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ipmi_edit_attributes.html.haml new file mode 100644 index 00000000000..99b038a6eb0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ipmi_edit_attributes.html.haml @@ -0,0 +1,23 @@ + +%input#proposal_attributes{:type => "hidden", :name => "proposal_attributes", :value => @proposal.raw_data['attributes'][@proposal.barclamp].to_json} +%p + %label{:for => "proposal_attributes"}= t('.attributes') + = link_to "Raw", proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => @dep_raw, :attr_raw => true), :style => "float: right;" + %div.container + + %p + %label{ :for => :bmc_enable }= t('.bmc_enable') + = select_tag :bmc_enable, options_for_select([['true','true'], ['false', 'false']], @proposal.raw_data['attributes'][@proposal.barclamp]["bmc_enable"].to_s), :onchange => "update_value('bmc_enable', 'bmc_enable', 'boolean')" + + %p + %label{ :for => :bmc_user }= t('.bmc_user') + %input#bmc_user{:type => "text", :name => "bmc_user", :'data-default' => @proposal.raw_data['attributes'][@proposal.barclamp]["bmc_user"], :onchange => "update_value('bmc_user', 'bmc_user', 'string')"} + + %p + %label{ :for => :bmc_password }= t('.bmc_password') + %input#bmc_password{:type => "text", :name => "bmc_password", :'data-default' => @proposal.raw_data['attributes'][@proposal.barclamp]["bmc_password"], :onchange => "update_value('bmc_password', 'bmc_password', 'string')"} + + %p + %label{ :for => :debug }= t('.debug') + = select_tag :debug, options_for_select([['true','true'], ['false', 'false']], @proposal.raw_data['attributes'][@proposal.barclamp]["debug"].to_s), :onchange => "update_value('debug', 'debug', 'boolean')" + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ipmi_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ipmi_edit_deployment.html.haml new file mode 100644 index 00000000000..786730b0025 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ipmi_edit_deployment.html.haml @@ -0,0 +1,12 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { + "ipmi-configure": { "unique": false, "count": -1 } + }; + node_selector($('#drag_drop'), constraints) + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_logging_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_logging_edit_deployment.html.haml new file mode 100644 index 00000000000..1c899b1958f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_logging_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "logging-server": { "unique": true, "count": 1 }, "logging-client": { "unique": true, "count": -1 } } + node_selector($('#drag_drop'), constraints) + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_nagios_edit_attributes.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_nagios_edit_attributes.html.haml new file mode 100644 index 00000000000..1ee57586746 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_nagios_edit_attributes.html.haml @@ -0,0 +1,7 @@ + +%input#proposal_attributes{:type => "hidden", :name => "proposal_attributes", :value => @proposal.raw_data['attributes'][@proposal.barclamp].to_json} +%p + %label{:for => "proposal_attributes"}= t('.attributes') + = link_to "Raw", proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => @dep_raw, :attr_raw => true), :style => "float: right;" + %div.container + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_nagios_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_nagios_edit_deployment.html.haml new file mode 100644 index 00000000000..d5d3c711f36 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_nagios_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "nagios-server": { "unique": true, "count": 1 }, "nagios-client": { "unique": true, "count": -1 } } + node_selector($('#drag_drop'), constraints) + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_network_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_network_edit_deployment.html.haml new file mode 100644 index 00000000000..a2966bc3abc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_network_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "network": { "unique": false, "count": -1 } }; + node_selector($('#drag_drop'), constraints); + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_node_selector.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_node_selector.html.haml new file mode 100644 index 00000000000..9d56c406092 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_node_selector.html.haml @@ -0,0 +1,85 @@ + +%input#proposal_development{:type => "hidden", :name => "proposal_deployment", :value => @proposal.raw_data['deployment'][@proposal.barclamp].to_json } +%p + %label{:for => "drag_drop"}= t('.title') + = link_to t('raw'), proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp, :dep_raw => true, :attr_raw => @attr_raw), :style => "float: right;" + %div{ :class => ["container"], :id => "drag_drop" } + .column_40.first + - @proposal.all_elements.each do |element| + %div{ :class => ["list"], :id => "#{element}_div" } + %div{ :class => ["list", "title"] }= "#{element}" + %ul.list{ :id => "#{element}" } + - unless @proposal.elements[element].nil? + - @proposal.elements[element].each do |v| + %li{ :name => "#{v}" }= v.split('.')[0] + + .column_50.last + %div{ :class => ["list"], :id => "nodes_div" } + %div{ :class => ["list", "title"] }= t('barclamp.proposal_show.available_nodes') + %ul + - NodeObject.all.each do |n| + %li{ :name => "#{n.name}" }= n.shortname + +.clear + +:javascript + function remove_item(item) { + var input = document.getElementById('proposal_development'); + var json = JSON.parse(input.value); + var name = item.attr('name'); + var listname = item.parent().parent().attr('id').replace("_div", ""); + var list = json['elements'][listname]; + var idx = list.indexOf(name); // Find the index + if (idx!=-1) list.splice(idx, 1) + input.value = JSON.stringify(json); + } + function copy_item(item, list) { + var input = document.getElementById('proposal_development'); + var json = JSON.parse(input.value); + var name = item.attr('name'); + if (json['elements'][list] == undefined) { + json['elements'][list] = new Array(); + } + json['elements'][list].push(name); + input.value = JSON.stringify(json); + } + function node_selector(base_item, constraints){ + // Set the default function + $.fn.drag_drop_multi_select.defaults.after_drop_action = function($item,$old,$new,e,ui){ + // Possible param $item_instance, $old_container, $new_container, event, helper + var $target = $(e.target); + var tid = $target.attr('id').replace("_div", ""); + if (tid != "nodes") { + var found = false; + var ul = $target.find('ul'); + var c = constraints[tid]; + ul.children().each(function(){ + if ($(this).attr('name') == $item.attr('name')) found = true; + }); + if (c["unique"]) { + $.each(constraints, function(key, value) { + $("#"+key).children().each(function(){ + if ($(this).attr('name') == $item.attr('name')) found = true; + }); + }); + } + if (!found && (c["count"] == -1 || c["count"] > ul.children().size())) { + copy_item($item, tid); + ul.append($.fn.drag_drop_multi_select_clone($item)); + } + } + else { + if ($item.parent().parent().attr('id') != $target.attr('id')) { + remove_item($item); + $item.remove(); + } + } + }; + // Initiate the drag & drop + base_item.drag_drop_multi_select({ + element_to_drag_drop_select:'.list ul li', + elements_to_drop:'.list', + elements_to_cancel_select:'.title' + }); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ntp_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ntp_edit_deployment.html.haml new file mode 100644 index 00000000000..9d812ff4ec5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_ntp_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "ntp-server": { "unique": true, "count": 1 }, "ntp-client": { "unique": true, "count": -1 } }; + node_selector($('#drag_drop'), constraints); + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_provisioner_edit_deployment.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_provisioner_edit_deployment.html.haml new file mode 100644 index 00000000000..119b0b27b22 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/_provisioner_edit_deployment.html.haml @@ -0,0 +1,10 @@ + +%p + = render :partial => "barclamp/node_selector" + +:javascript + $(document).ready(function(){ + var constraints = { "provisioner-server": { "unique": false, "count": 1 }, "provisioner-base": { "unique": false, "count": -1 } }; + node_selector($('#drag_drop'), constraints); + }); + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_index.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_index.html.haml new file mode 100644 index 00000000000..0fcca66d020 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_index.html.haml @@ -0,0 +1,31 @@ +%h1= t('.title') + +%table.data.box + %thead + %tr + %th= ProposalObject.human_attribute_name :name + %th= RoleObject.human_attribute_name :proposal_count + %th= ProposalObject.human_attribute_name :description + + %tbody + - @barclamps.sort.each do |entry| + %tr{ :class => cycle(:odd, :even), :id => entry[0] } + %td= link_to entry[0].titlecase, barclamp_show_barclamp_path(entry[0]) + %td.count= t '.none' + %td= entry[1] + +:javascript + function onload() { + update(); + } + + function update() { + $.getJSON('#{status_barclamp_path}', function(data) { + + // Update roles + $.each(data['barclamps'], function(key,val) { + $('#'+key+' .count').text((parseInt(val)==0 ? '#{t ".none"}' : val)); + }); + + }); + } \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_proposals.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_proposals.html.haml new file mode 100644 index 00000000000..71c5ea4f0ce --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_proposals.html.haml @@ -0,0 +1,43 @@ +%h1= t('.title') + +%table.data.box + %thead + %tr + %th= ProposalObject.human_attribute_name :barclamp + %th= ProposalObject.human_attribute_name :status + %th= ProposalObject.human_attribute_name :name + %th= ProposalObject.human_attribute_name :description + + %tbody + - @proposals.sort_by{|n| "#{n.barclamp}/#{n.name}" }.each do |proposal| + %tr{ :class => ["proposal", cycle(:odd, :even)], :id => "#{proposal.barclamp}_#{proposal.name}" } + %td= link_to proposal.barclamp.titlecase, barclamp_show_barclamp_path(proposal.barclamp) + %td.status + .led{:class => proposal.status, :title => proposal.status} + %td= link_to proposal.name, proposal_barclamp_path(:id => proposal.name, :controller => proposal.barclamp) + %td= proposal.description + +:javascript + function update() { + $.getJSON('#{status_proposals_barclamp_path}', function(data) { + + // If proposal count has changed, re-render the page + var current_proposals = $('.proposal').length; + if(data['count'] != current_proposals) { + location.reload(); + } + + // Update proposals + $.each(data['proposals'], function(key,val) { + var td = $('#'+key+' td'); + var led = td.children('.led'); + if(!led.hasClass(val)){ + led.attr('class', 'led '+val); + led.attr('title', val); + td.effect("highlight", {color: '#666666'}, 2000); + } + }); + + }); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_roles.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_roles.html.haml new file mode 100644 index 00000000000..27a8207abb4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_roles.html.haml @@ -0,0 +1,29 @@ +%h1= t('.title') + +%table.data.box + %thead + %tr + %th= RoleObject.human_attribute_name :barclamp + %th= RoleObject.human_attribute_name :name + %th= RoleObject.human_attribute_name :description + + %tbody + - @roles.sort_by{|n| "#{n.barclamp}/#{n.name}" }.each do |role| + %tr{ :class => ["role", cycle(:odd, :even)], :id => "#{role.barclamp}_#{role.name}" } + %td= link_to role.barclamp.titlecase, barclamp_show_barclamp_path(role.barclamp) + %td= link_to role.inst, show_barclamp_path(:id => role.inst, :controller => role.barclamp) + %td= role.description + +:javascript + function update() { + $.getJSON('#{status_barclamp_path}', function(data) { + + // If role has changed, re-render the page + var current_roles = $('.role').length; + if(data['count'] != current_roles) { + location.reload(); + } + + }); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_show.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_show.html.haml new file mode 100644 index 00000000000..25384917f48 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/barclamp_show.html.haml @@ -0,0 +1,25 @@ +%h1= t '.title' +%h2= @barclamp.bc_name.titlecase + +%h3= t '.create_proposal' +.box + -# NOTE: There is a small change for RAILS2! + - form_for :proposal, :remote => true, :url => create_proposal_barclamp_path(:controller => @barclamp.bc_name), :html => { :'data-type' => 'html', :'data-method' => 'put', :method => :put, :id => 'create_proposal_form', :class => "formtastic"} do |f| + = hidden_field_tag :barclamp, @barclamp.bc_name + %fieldset.inputs + %ol + %li.text + = f.label :name, t('.name') + = text_field_tag :name, t('.default') + %li.text + = f.label :description, t('.description') + = text_field_tag :description, "#{t 'created_on'} #{l(Time.now) }", :size => 60 + %input.button{:type => "submit", :value => t('.create_proposal')} + +%h3= t '.active' +- @barclamp.list_active[1].each do |role| + = dl_item link_to(role, show_barclamp_path(:id => role, :controller => @barclamp.bc_name)), role, {:escape_html=>false} + +%h3= t '.proposals' +- @barclamp.proposals[1].each do |proposal| + = dl_item link_to(proposal, proposal_barclamp_path(:id => proposal, :controller => @barclamp.bc_name)), proposal, {:escape_html=>false} diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/index.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/index.html.haml new file mode 100644 index 00000000000..7518aaaeb65 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/index.html.haml @@ -0,0 +1,39 @@ +%h1= "#{t('.title')} #{@bc_name.titlecase}" + +%table.data.box + %thead + %tr + %th= RoleObject.human_attribute_name :barclamp + %th= RoleObject.human_attribute_name :name + %th= RoleObject.human_attribute_name :description + + %tbody + - @roles.sort_by{|n| "#{n.barclamp}/#{n.name}" }.each do |role| + %tr{ :class => ["role", cycle(:odd, :even)], :id => "#{role.barclamp}_#{role.name}" } + %td= link_to role.barclamp, barclamp_show_barclamp_path(role.barclamp) + %td= link_to role.inst, show_barclamp_path(:id => role.inst, :controller => role.barclamp) + %td= role.description + +:javascript + function update() { + $.getJSON('#{status_barclamp_path}', function(data) { + + // If role has changed, re-render the page + var current_roles = $('.role').length; + if(data['count'] != current_roles) { + location.reload(); + } + + // Update roles + $.each(data['roles'], function(key,val) { + var td = $('#'+key+' td'); + var led = td.children('.led'); + if(!led.hasClass(val)){ + led.attr('class', 'led '+val); + td.effect("highlight", {color: '#666666'}, 2000); + } + }); + + }); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposal_index.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposal_index.html.haml new file mode 100644 index 00000000000..50f0fa36bab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposal_index.html.haml @@ -0,0 +1,42 @@ +%h1= "#{t('.title')} #{@bc_name.titlecase}" + +%table.data.box + %thead + %tr + %th= ProposalObject.human_attribute_name :barclamp + %th= ProposalObject.human_attribute_name :status + %th= ProposalObject.human_attribute_name :name + %th= ProposalObject.human_attribute_name :description + + %tbody + - @proposals.sort_by{|n| "#{n.barclamp}/#{n.name}" }.each do |proposal| + %tr{ :class => ["proposal", cycle(:odd, :even)], :id => "#{proposal.barclamp}_#{proposal.name}" } + %td= link_to proposal.barclamp.titlecase, barclamp_show_barclamp_path(proposal.barclamp) + %td.status + .led{:class => proposal.status} + %td= link_to proposal.name, proposal_barclamp_path(:id => proposal.name, :controller => proposal.barclamp) + %td= proposal.description + +:javascript + function update() { + $.getJSON('#{status_proposals_barclamp_path}', function(data) { + + // If proposal count has changed, re-render the page + var current_proposals = $('.proposal').length; + if(data['count'] != current_proposals) { + location.reload(); + } + + // Update proposals + $.each(data['proposals'], function(key,val) { + var td = $('#'+key+' td'); + var led = td.children('.led'); + if(!led.hasClass(val)){ + led.attr('class', 'led '+val); + td.effect("highlight", {color: '#666666'}, 2000); + } + }); + + }); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposal_show.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposal_show.html.haml new file mode 100644 index 00000000000..34ed463136a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposal_show.html.haml @@ -0,0 +1,26 @@ +%h1= t '.title' + +-# Rails 2 needs - and Rails 3 needs = +- form_for :proposal, :url => update_proposal_barclamp_path(:id => @proposal.name, :controller => @proposal.barclamp), :html => { :method => :post, :'data-type' => 'html', :id => 'update_proposal_form', :class => "box" } do |f| + %h2= "#{@proposal.barclamp.titlecase} #{@proposal.name.titlecase}" + + %input#barclamp{:type => "hidden", :name => "barclamp", :value => @proposal.barclamp} + %input#name{:type => "hidden", :name => "name", :value => @proposal.name} + + =(@attr_raw ? (render :partial => 'barclamp/edit_attributes_raw') : (begin render :partial => "barclamp/#{@proposal.barclamp}_edit_attributes" rescue (render :partial => 'barclamp/edit_attributes_raw') end)) + =# render :partial => "barclamp/#{@proposal.barclamp}_edit_attributes" + = render :partial => "barclamp/attribute_helper" + + =(@dep_raw ? (render :partial => 'barclamp/edit_deployment_raw') : (begin render :partial => "barclamp/#{@proposal.barclamp}_edit_deployment" rescue (render :partial => 'barclamp/edit_deployment_raw') end)) + =# render :partial => "barclamp/#{@proposal.barclamp}_edit_deployment" + + %p + - if @proposal["deployment"][@proposal.barclamp]["crowbar-committing"].nil? or !@proposal["deployment"][@proposal.barclamp]["crowbar-committing"] + %input.button{:type => "submit", :name => "submit", :value => t('.save_proposal')} + %input.button{:type => "submit", :name => "submit", :value => t('.commit_proposal'), :'data-confirm' => t('.apply_changes')} + - unless @proposal["deployment"][@proposal.barclamp]["crowbar-queued"] + %input.button{:type => "submit", :name => "submit", :value => t('.delete_proposal'), :'data-confirm' => t('.delete_changes')} + - if @proposal["deployment"][@proposal.barclamp]["crowbar-queued"] + %input.button{:type => "submit", :name => "submit", :value => t('.dequeue_proposal')} + + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposals.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposals.html.haml new file mode 100644 index 00000000000..1889d1dc99e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/proposals.html.haml @@ -0,0 +1,46 @@ +%h1= t('.title') + +%table.data.box + %thead + %tr + %th= ProposalObject.human_attribute_name :barclamp + %th= ProposalObject.human_attribute_name :status + %th= ProposalObject.human_attribute_name :name + %th= ProposalObject.human_attribute_name :description + + %tbody + - @proposals.sort_by{|n| "#{n.barclamp}/#{n.name}" }.each do |proposal| + %tr{ :class => ["proposal", cycle(:odd, :even)], :id => "#{proposal.barclamp}_#{proposal.name}" } + %td= link_to proposal.barclamp.titlecase, barclamp_path(proposal.barclamp) + %td.status + .led{:class => proposal.status, :title => proposal.status} + %td= link_to proposal.name, proposal_path(proposal.id) + %td= proposal.description + -# %td.action= link_to t('edit'), edit_proposal_path(proposal) + -# %td.action= link_to t('destroy'), proposal, :confirm => t('are_you_sure'), :method => :delete + + +:javascript + function update() { + $.getJSON('#{status_proposals_path}', function(data) { + + // If proposal count has changed, re-render the page + var current_proposals = $('.proposal').length; + if(data['count'] != current_proposals) { + location.reload(); + } + + // Update proposals + $.each(data['proposals'], function(key,val) { + var td = $('#'+key+' td'); + var led = td.children('.led'); + if(!led.hasClass(val)){ + led.attr('class', 'led '+val); + led.attr('title', val) + td.effect("highlight", {color: '#666666'}, 2000); + } + }); + + }); + } + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/barclamp/show.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/show.html.haml new file mode 100644 index 00000000000..2224f649b3f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/barclamp/show.html.haml @@ -0,0 +1,13 @@ +%h1= "#{@role.barclamp.titlecase} #{@role.inst}" + +.box + %h2= t '.attributes' + - @role.default_attributes[@role.barclamp].each_pair do |key, value| + = dl_item "#{t '.key'} #{key}", (value.is_a?(Enumerable) ? hash_to_ul(value) : value), {:escape_html=>false} #hash_to_ul(value).html_safe + +.box + %h2= t '.deployment' + - @role.elements.each_pair do |key, value| + = dl_item "#{t '.role'} #{key}", (value.map{|m| m.split('.').first}.map{|m| link_to m, nodes_path(:selected => m)}.join('
    ')), {:escape_html=>false} #.html_safe + += link_to t('.delete_role'), delete_barclamp_path(:controller => @role.barclamp, :id => @role.inst), :method => :delete, :class => 'button', :'data-remote' => true diff --git a/crowbar/change-image/dell/openstack_manager/app/views/layouts/application.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/layouts/application.html.haml new file mode 100644 index 00000000000..acd4bf2661a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/layouts/application.html.haml @@ -0,0 +1,34 @@ +!!! +%html + %head + %title= "Crowbar: #{ controller.action_name }" + = stylesheet_link_tag 'application' + /[if IE] + -# = stylesheet_link_tag 'ie', :media => "all" + = javascript_include_tag "html5" + -# ALWAYS DIFFERENT FROM RAILS3 + = javascript_include_tag 'jquery.min', 'jquery-ui.min', 'jquery.sparkline.min', 'jquery.drag_drop_multi_select_alpha', 'rails', 'codemirror/codemirror', 'codemirror/javascript', 'application' + = csrf_meta_tag + + %body + %header + .container + %h1 + %a{:href => "/"} + %span logo + %nav + = render_navigation + + - if flash[:notice] || !CHEF_ONLINE + .notice + .container + %p= flash[:notice] + - if !CHEF_ONLINE + %p= I18n.t "offline" + + .container + = yield + + %footer + Provided by Dell, Inc. + CloudEdge Solutions team — Version #{CROWBAR_VERSION} diff --git a/crowbar/change-image/dell/openstack_manager/app/views/machines/_node.html.erb b/crowbar/change-image/dell/openstack_manager/app/views/machines/_node.html.erb new file mode 100644 index 00000000000..3498c0f72cd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/machines/_node.html.erb @@ -0,0 +1,44 @@ +<% + require 'uri' + node ||= @node + name = @name || node.name.split('.')[0] + unless node[:crowbar].nil? or node[:crowbar][:network].nil? or node[:crowbar][:network][:admin].nil? + ip = node[:crowbar][:network][:admin][:address] || "unknown" + else + ip = node[:ipaddress] || "unknown" + end + icon = node[:state] || "unknown" + show_switch=false + unless node[:crowbar].nil? or node[:crowbar][:switch_config].nil? + show_switch=true + intf = node.get_network_by_type("admin")["interface"] + sw_port = node[:crowbar][:switch_config][intf][:switch_port] + sw_unit = node[:crowbar][:switch_config][intf][:switch_unit] + sw_name = node[:crowbar][:switch_config][intf][:switch_name] + show_switch = false if sw_name == "-1" + end + state = icon.capitalize +%> + + + + <%= name %> + + +<%= ip %> +<% if show_switch -%> +<%= sw_name %> <%= sw_unit %>/<%= sw_port %> +<% else -%> + - +<% end -%> + +<% unless name == "admin" -%> + + + + + + +<% end -%> + + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/machines/index.html.erb b/crowbar/change-image/dell/openstack_manager/app/views/machines/index.html.erb new file mode 100644 index 00000000000..5d57e2aa2ae --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/machines/index.html.erb @@ -0,0 +1,18 @@ +<% unless @app.empty? %> +
    +

    System: <%= pluralize(@app.size, "Node") %>

    + + + <% nodes = @app + nodes = nodes.sort{|a, b| b.name <=> a.name} + nodes.each do |node| %> + <% name = node.name.split('.')[0] %> + + <%= render :partial=>"node", :locals => {:node => node, :name => name } %> + + <% end %> +
    +
    +<% else %> +
    <%= link_to "Retry Connection?", {:controller=>:main, :action=>:index } %>
    +<% end %> diff --git a/crowbar/change-image/dell/openstack_manager/app/views/machines/transition.erb b/crowbar/change-image/dell/openstack_manager/app/views/machines/transition.erb new file mode 100644 index 00000000000..bfc95458baf --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/machines/transition.erb @@ -0,0 +1,2 @@ +Success +<%= @machine.inspect %> diff --git a/crowbar/change-image/dell/openstack_manager/app/views/machines/transition.yaml.erb b/crowbar/change-image/dell/openstack_manager/app/views/machines/transition.yaml.erb new file mode 100644 index 00000000000..35821117c87 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/machines/transition.yaml.erb @@ -0,0 +1 @@ +Success diff --git a/crowbar/change-image/dell/openstack_manager/app/views/nodes/_form.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/nodes/_form.html.haml new file mode 100644 index 00000000000..0a04df3c6f7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/nodes/_form.html.haml @@ -0,0 +1,23 @@ +.box + -# Rails 2 needs - and Rails 3 needs = + - form_for :node, :'data-remote' => true, :url => update_node_path(@node.shortname), :html => { :'data-type' => 'html', :'data-method' => 'put', :id => 'update_node_form', :class => "formtastic" } do |f| + %fieldset.inputs + %ol + %li.text + = f.label :description, t('.description') + = text_field_tag :description, "#{t 'created_on'} #{l(Time.now)}", :size => 60 + %li.select + = f.label :usage, t('.usage') + = select_tag :usage, options_for_select([['Swift','swift'], ['Nova','nova'], ['Glance','glance']], @node.usage.first || 'nova') + %li.select + = f.label :bios, t('.bios') + = select_tag :bios, options_for_select([['Virtualization','Virtualization'], ['Storage','Storage']], @node.bios_set || 'Virtualization') + %li.select + = f.label :raid, t('.raid') + = select_tag :raid, options_for_select([['JBOD Only','JBODOnly'], ['Single RAID 10','SingleRaid10']], @node.raid_set || 'SingleRaid10') + %fieldset.buttons + %ol + %li + %input.button{:type => "submit", :name => "submit", :value => t('.allocate')} + %li + %input.button{:type => "submit", :name => "submit", :value => t('.save')} diff --git a/crowbar/change-image/dell/openstack_manager/app/views/nodes/_show.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/nodes/_show.html.haml new file mode 100644 index 00000000000..c09e27ab12f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/nodes/_show.html.haml @@ -0,0 +1,54 @@ +%h1{:style=>"float: left"}= "#{@node.shortname} (#{link_to t('edit'), edit_node_path(@node.shortname)})"#.html_safe + +%ul.buttons + %li= link_to "Reboot", hit_node_path(@node.shortname, 'reboot'), :class => 'button', :'data-remote' => true + %li= link_to "Shutdown", hit_node_path(@node.shortname, 'shutdown'), :class => 'button', :'data-remote' => true + %li= link_to "Power On", hit_node_path(@node.shortname, 'poweron'), :class => 'button', :'data-remote' => true + %li= link_to "Identify", hit_node_path(@node.shortname, 'identify'), :class => 'button', :'data-remote' => true + +.column_50.first + %dl + = dl_item((NodeObject.human_attribute_name :name), @node.name) + = dl_item((NodeObject.human_attribute_name :state), t(@node.state, :scope => :state)) + = dl_item((NodeObject.human_attribute_name :uptime), (@node.ready? ? @node.uptime : NodeObject.human_attribute_name(:na))) + = dl_item((NodeObject.human_attribute_name :switch_name_port), "#{@node.switch_name} / #{@node.switch_port}") + = dl_item((NodeObject.human_attribute_name :mac), @node.mac) + = dl_item((NodeObject.human_attribute_name :allocated), (@node.allocated ? t('.active') : t('.inactive'))) + +.column_50.last + %dl + = dl_item((NodeObject.human_attribute_name :description), @node.description) + = dl_item((NodeObject.human_attribute_name :hardware), @node.hardware) + = dl_item((NodeObject.human_attribute_name :cpu), @node.cpu) + = dl_item((NodeObject.human_attribute_name :memory), format_memory(@node.memory)) + = dl_item((NodeObject.human_attribute_name :number_of_drives), @node.number_of_drives) + = dl_item((NodeObject.human_attribute_name :asset_tag), @node.asset_tag) + +.clear + +%dl.networks + = dl_item((NodeObject.human_attribute_name :ip), "
      #{ip_addresses(@network)}
    ", {:escape_html=>false}) + +%dl.links + %dt + = t('.links') + %dd + = link_to t('.bmc'), "https://#{@node.get_network_by_type("bmc")["address"]}", :target => '_blank' + - unless @node["crowbar"]["links"].nil? + - @node["crowbar"]["links"].each do |name,link| + , + = link_to name, link, :target => '_blank' + +%dl.barclamps + = dl_item((NodeObject.human_attribute_name :barclamps), barclamps_list(@node.roles), {:escape_html=>false}) + +%dl.roles + = dl_item((NodeObject.human_attribute_name :roles), roles_list(@node.roles), {:escape_html=>false}) + +%ul.buttons + - unless @node.admin? + %li= link_to "Hardware Update", hit_node_path(@node.shortname, 'update'), :class => 'button', :'data-remote' => true + %li= link_to "Reinstall", hit_node_path(@node.shortname, 'reinstall'), :class => 'button', :'data-remote' => true + %li= link_to "Reset", hit_node_path(@node.shortname, 'reset'), :class => 'button', :'data-remote' => true + %li= link_to "Delete", hit_node_path(@node.shortname, 'delete'), :class => 'button', :'data-remote' => true + diff --git a/crowbar/change-image/dell/openstack_manager/app/views/nodes/edit.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/nodes/edit.html.haml new file mode 100644 index 00000000000..0d83d83c624 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/nodes/edit.html.haml @@ -0,0 +1,4 @@ +%h1= @node.name +%p.links + = link_to t('back'), nodes_path(:selected => @node.shortname) += render 'form' diff --git a/crowbar/change-image/dell/openstack_manager/app/views/nodes/index.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/nodes/index.html.haml new file mode 100644 index 00000000000..039133ba817 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/nodes/index.html.haml @@ -0,0 +1,72 @@ +.column_100 + - unless @nodes.empty? + %section.box#details + - if @node + = render :partial => 'show' + - else + %p= t("status_empty", :scope=>"nodes.index") % @nodes.count + +#nodes + - @nodes.group_by(&:switch_name).each_with_index do |switch, i| + .column_25{:class => column_class(i,4)} + %table.data.box{:id => "switch_#{switch[0].parameterize}"} + %thead + %tr + %th.status{:title => ''} + - status = NodeObject.status(switch[1]) + .inline_piechart= [status["ready"], status["unready"], status["unknown"], status["pending"]] * "," + %th= switch[0] == -1 ? 'Unknown' : switch[0] + + %tbody + - switch[1].sort_by{|n| n.location.to_i }.each do |node| + %tr{ :class => ["node", cycle(:odd, :even, :name => "nodes_#{i}")], :id => node.shortname } + %td.status + .led{:class => node.status, :title=> node.state} + %td + %a{:href => node_path(node.shortname), :title=> "#{NodeObject.human_attribute_name :description}: #{node.description}", :class => 'node_details', :id => node.shortname.parameterize}= node.shortname + +.clear + +:javascript + var racks = {}; + function update() { + $.getJSON('#{nodes_status_path}', function(data) { + + // If node count has changed, re-render the page + var current_nodes = $('.node').length; + if(data['count'] != current_nodes) { + location.reload(); + } + + // Update switch-level piecharts + $.each(data['switches'], function(key,val) { + var values = val['ready'] + ', ' + val['unready'] + ', ' + val['unknown'] + ', ' + val['pending']; + var total = val['ready']+val['unready']+val['unknown']+val['pending']; + if (racks[key]==undefined) { + racks[key] = total; + } else if (racks[key]!=total) { + location.reload(); + } + $('#switch_'+key.toLowerCase()+' th.status .inline_piechart').html(values).sparkline('html', piechart_options ); + var status = "#{t :status_pie, :scope=>'nodes.index'}"; + status = status.replace("{0}", total); + status = status.replace("{1}", val['ready']); + status = status.replace("{2}", val['unready']); + status = status.replace("{3}", val['unknown']); + status = status.replace("{4}", val['pending']); + $('#switch_'+key.toLowerCase()+' th.status').attr('title', status) + }); + + // Update nodes + $.each(data['nodes'], function(key,val) { + var td = $('#'+key+' td'); + var led = td.children('.led'); + if(!led.hasClass(val['status'])){ + led.attr('class', 'led '+val['status']); + td.effect("highlight", {color: '#666666'}, 2000); + } + led.attr('title', val['state']); + }); + + }); + } diff --git a/crowbar/change-image/dell/openstack_manager/app/views/nodes/new.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/nodes/new.html.haml new file mode 100644 index 00000000000..39abc9b3b77 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/nodes/new.html.haml @@ -0,0 +1,3 @@ +%h1= t '.title' +%p.links= link_to t('back'), nodes_path += render 'form' diff --git a/crowbar/change-image/dell/openstack_manager/app/views/nodes/show.html.haml b/crowbar/change-image/dell/openstack_manager/app/views/nodes/show.html.haml new file mode 100644 index 00000000000..a9ba1504892 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/app/views/nodes/show.html.haml @@ -0,0 +1 @@ += render :partial => 'show' diff --git a/crowbar/change-image/dell/openstack_manager/config/boot.rb b/crowbar/change-image/dell/openstack_manager/config/boot.rb new file mode 100644 index 00000000000..dd5e3b69164 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/boot.rb @@ -0,0 +1,110 @@ +# Don't change this file! +# Configure your app in config/environment.rb and config/environments/*.rb + +RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) + +module Rails + class << self + def boot! + unless booted? + preinitialize + pick_boot.run + end + end + + def booted? + defined? Rails::Initializer + end + + def pick_boot + (vendor_rails? ? VendorBoot : GemBoot).new + end + + def vendor_rails? + File.exist?("#{RAILS_ROOT}/vendor/rails") + end + + def preinitialize + load(preinitializer_path) if File.exist?(preinitializer_path) + end + + def preinitializer_path + "#{RAILS_ROOT}/config/preinitializer.rb" + end + end + + class Boot + def run + load_initializer + Rails::Initializer.run(:set_load_path) + end + end + + class VendorBoot < Boot + def load_initializer + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" + Rails::Initializer.run(:install_gem_spec_stubs) + Rails::GemDependency.add_frozen_gem_path + end + end + + class GemBoot < Boot + def load_initializer + self.class.load_rubygems + load_rails_gem + require 'initializer' + end + + def load_rails_gem + if version = self.class.gem_version + gem 'rails', version + else + gem 'rails' + end + rescue Gem::LoadError => load_error + $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) + exit 1 + end + + class << self + def rubygems_version + Gem::RubyGemsVersion rescue nil + end + + def gem_version + if defined? RAILS_GEM_VERSION + RAILS_GEM_VERSION + elsif ENV.include?('RAILS_GEM_VERSION') + ENV['RAILS_GEM_VERSION'] + else + parse_gem_version(read_environment_rb) + end + end + + def load_rubygems + min_version = '1.3.2' + require 'rubygems' + unless rubygems_version >= min_version + $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.) + exit 1 + end + + rescue LoadError + $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org) + exit 1 + end + + def parse_gem_version(text) + $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/ + end + + private + def read_environment_rb + File.read("#{RAILS_ROOT}/config/environment.rb") + end + end + end +end + +# All that for this: +Rails.boot! diff --git a/crowbar/change-image/dell/openstack_manager/config/cucumber.yml b/crowbar/change-image/dell/openstack_manager/config/cucumber.yml new file mode 100644 index 00000000000..621a14cea66 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/cucumber.yml @@ -0,0 +1,8 @@ +<% +rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" +rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" +std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@wip" +%> +default: <%= std_opts %> features +wip: --tags @wip:3 --wip features +rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip diff --git a/crowbar/change-image/dell/openstack_manager/config/database.yml b/crowbar/change-image/dell/openstack_manager/config/database.yml new file mode 100644 index 00000000000..4ae71059e70 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/database.yml @@ -0,0 +1,26 @@ +# Crowbar does NOT use a database. This file is not used. +# SQLite version 3.x +# gem install sqlite3-ruby (not necessary on OS X Leopard) +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: &test + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 + +cucumber: + <<: *test \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/config/environment.rb b/crowbar/change-image/dell/openstack_manager/config/environment.rb new file mode 100644 index 00000000000..6917ccaf2a6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/environment.rb @@ -0,0 +1,55 @@ +# Be sure to restart your server when you modify this file + +# Specifies gem version of Rails to use when vendor/rails is not present +RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION + +# Bootstrap the Rails environment, frameworks, and default configuration +require File.join(File.dirname(__FILE__), 'boot') + +Rails::Initializer.run do |config| + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Add additional load paths for your own custom dirs + # config.load_paths += %W( #{RAILS_ROOT}/extras ) + + # Specify gems that this application depends on and have them installed with rake gems:install + # config.gem "bj" + # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" + # config.gem "sqlite3-ruby", :lib => "sqlite3" + # config.gem "aws-s3", :lib => "aws/s3" + config.gem "haml" + config.gem "sass" + config.gem "simple-navigation" + config.gem "i18n" + config.gem "json", :version => '1.5.2' + + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + + # Skip frameworks you're not going to use. To use Rails without a database, + # you must remove the Active Record framework. + # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] + config.frameworks -= [ :active_record ] + + # Activate observers that should always be running + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. + config.time_zone = 'UTC' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] + # config.i18n.default_locale = :de + + CHEF_CLIENT_KEY = "/opt/dell/openstack_manager/config/client.pem" unless defined? CHEF_CLIENT_KEY + CHEF_NODE_NAME ="crowbar" unless defined? CHEF_NODE_NAME + CHEF_SERVER_URL = "http://localhost:4000" unless defined? CHEF_SERVER_URL + CHEF_ONLINE = true unless defined? CHEF_ONLINE + OFFLINE_FILES_DIR = 'db' unless defined? OFFLINE_FILES_DIR + CROWBAR_VERSION = '0.0.1' unless defined? CROWBAR_VERSION +end diff --git a/crowbar/change-image/dell/openstack_manager/config/environments/cucumber.rb b/crowbar/change-image/dell/openstack_manager/config/environments/cucumber.rb new file mode 100644 index 00000000000..8ea9b6850ac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/environments/cucumber.rb @@ -0,0 +1,33 @@ +# Edit at your own peril - it's recommended to regenerate this file +# in the future when you upgrade to a newer version of Cucumber. + +# IMPORTANT: Setting config.cache_classes to false is known to +# break Cucumber's use_transactional_fixtures method. +# For more information see https://rspec.lighthouseapp.com/projects/16211/tickets/165 +config.cache_classes = true + +# Log error messages when you accidentally call methods on nil. +config.whiny_nils = true + +# Show full error reports and disable caching +config.action_controller.consider_all_requests_local = true +config.action_controller.perform_caching = false + +# Disable request forgery protection in test environment +config.action_controller.allow_forgery_protection = false + +# Tell Action Mailer not to deliver emails to the real world. +# The :test delivery method accumulates sent emails in the +# ActionMailer::Base.deliveries array. +config.action_mailer.delivery_method = :test + +config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails')) +config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner')) +config.gem 'capybara', :lib => false, :version => '>=0.3.5' unless File.directory?(File.join(Rails.root, 'vendor/plugins/capybara')) + +CHEF_SERVER_URL = "http://localhost:4000" +CHEF_CLIENT_KEY = "tmp\\chefkey.pem" +CHEF_NODE_NAME = "WN7-J84NHL1" +CHEF_ONLINE = true +CROWBAR_VERSION = "cucumber" +OFFLINE_FILES_DIR = File.join('test', 'db') diff --git a/crowbar/change-image/dell/openstack_manager/config/environments/development.rb b/crowbar/change-image/dell/openstack_manager/config/environments/development.rb new file mode 100644 index 00000000000..bd894218fd4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/environments/development.rb @@ -0,0 +1,42 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# Settings specified here will take precedence over those in config/environment.rb + +# In the development environment your application's code is reloaded on +# every request. This slows down response time but is perfect for development +# since you don't have to restart the webserver when you make code changes. +config.cache_classes = false + +# Log error messages when you accidentally call methods on nil. +config.whiny_nils = true + +# Show full error reports and disable caching +config.action_controller.consider_all_requests_local = true +config.action_view.debug_rjs = true +config.action_controller.perform_caching = false + +# Don't care if the mailer can't send +config.action_mailer.raise_delivery_errors = false + +CHEF_SERVER_URL = "http://localhost:4000" +CHEF_CLIENT_KEY = "tmp\\chefkey.pem" +CHEF_NODE_NAME = "WN7-J84NHL1" +CHEF_ONLINE = true +CROWBAR_VERSION = "Development" +CHEF_ONLINE = true +OFFLINE_FILES_DIR = 'db' + diff --git a/crowbar/change-image/dell/openstack_manager/config/environments/production.rb b/crowbar/change-image/dell/openstack_manager/config/environments/production.rb new file mode 100644 index 00000000000..a4f39a3d933 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/environments/production.rb @@ -0,0 +1,53 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# + +# Settings specified here will take precedence over those in config/environment.rb + +# The production environment is meant for finished, "live" apps. +# Code is not reloaded between requests +config.cache_classes = true + +# Full error reports are disabled and caching is turned on +config.action_controller.consider_all_requests_local = false +config.action_controller.perform_caching = true +config.action_view.cache_template_loading = true + +# See everything in the log (default is :info) +# config.log_level = :debug + +# Use a different logger for distributed setups +require 'syslogger' +config.logger = Syslogger.new("crowbar_app", Syslog::LOG_PID, Syslog::LOG_LOCAL0) + +# Use a different cache store in production +# config.cache_store = :mem_cache_store + +# Enable serving of images, stylesheets, and javascripts from an asset server +# config.action_controller.asset_host = "http://assets.example.com" + +# Disable delivery errors, bad email addresses will be ignored +# config.action_mailer.raise_delivery_errors = false + +# Enable threaded mode +# config.threadsafe! + + CHEF_CLIENT_KEY = "/opt/dell/openstack_manager/config/client.pem" + CHEF_NODE_NAME ="crowbar" + CHEF_SERVER_URL = "http://localhost:4000" + CHEF_ONLINE = true + CROWBAR_VERSION = "Production" + diff --git a/crowbar/change-image/dell/openstack_manager/config/environments/test.rb b/crowbar/change-image/dell/openstack_manager/config/environments/test.rb new file mode 100644 index 00000000000..aeda19e660b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/environments/test.rb @@ -0,0 +1,44 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +# Settings specified here will take precedence over those in config/environment.rb + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! +config.cache_classes = true + +# Log error messages when you accidentally call methods on nil. +config.whiny_nils = true + +# Show full error reports and disable caching +config.action_controller.consider_all_requests_local = true +config.action_controller.perform_caching = false +config.action_view.cache_template_loading = true + +# Disable request forgery protection in test environment +config.action_controller.allow_forgery_protection = false + +# Tell Action Mailer not to deliver emails to the real world. +# The :test delivery method accumulates sent emails in the +# ActionMailer::Base.deliveries array. +config.action_mailer.delivery_method = :test + +# Use SQL instead of Active Record's schema dumper when creating the test database. +# This is necessary if your schema can't be completely dumped by the schema dumper, +# like if you have constraints or database-specific column types +# config.active_record.schema_format = :sql diff --git a/crowbar/change-image/dell/openstack_manager/config/initializers/backtrace_silencers.rb b/crowbar/change-image/dell/openstack_manager/config/initializers/backtrace_silencers.rb new file mode 100644 index 00000000000..c2169ed01c5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying do debug a problem that might steem from framework code. +# Rails.backtrace_cleaner.remove_silencers! \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/config/initializers/inflections.rb b/crowbar/change-image/dell/openstack_manager/config/initializers/inflections.rb new file mode 100644 index 00000000000..d531b8bb825 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/initializers/inflections.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format +# (all these examples are active by default): +# ActiveSupport::Inflector.inflections do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end diff --git a/crowbar/change-image/dell/openstack_manager/config/initializers/mime_types.rb b/crowbar/change-image/dell/openstack_manager/config/initializers/mime_types.rb new file mode 100644 index 00000000000..72aca7e441e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/initializers/mime_types.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf +# Mime::Type.register_alias "text/html", :iphone diff --git a/crowbar/change-image/dell/openstack_manager/config/initializers/new_rails_defaults.rb b/crowbar/change-image/dell/openstack_manager/config/initializers/new_rails_defaults.rb new file mode 100644 index 00000000000..c94db0a6642 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/initializers/new_rails_defaults.rb @@ -0,0 +1,21 @@ +# Be sure to restart your server when you modify this file. + +# These settings change the behavior of Rails 2 apps and will be defaults +# for Rails 3. You can remove this initializer when Rails 3 is released. + +if defined?(ActiveRecord) + # Include Active Record class name as root for JSON serialized output. + ActiveRecord::Base.include_root_in_json = true + + # Store the full class name (including module namespace) in STI type column. + ActiveRecord::Base.store_full_sti_class = true +end + +ActionController::Routing.generate_best_match = false + +# Use ISO 8601 format for JSON serialized times and dates. +ActiveSupport.use_standard_json_time_format = true + +# Don't escape HTML entities in JSON, leave that for the #json_escape helper. +# if you're including raw json in an HTML page. +ActiveSupport.escape_html_entities_in_json = false \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/config/initializers/session_store.rb b/crowbar/change-image/dell/openstack_manager/config/initializers/session_store.rb new file mode 100644 index 00000000000..f45dc2f5370 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/initializers/session_store.rb @@ -0,0 +1,15 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying cookie session data integrity. +# If you change this key, all old sessions will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +ActionController::Base.session = { + :key => '_openstack_manager_session', + :secret => 'ae66894fcee606d20eab9637a28684a8e9a12d36a91d075035e5703ed49bb26a0f9163fd0954185dea2b701cf79cefad152fb6da0075af43066b790707f52424' +} + +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rake db:sessions:create") +# ActionController::Base.session_store = :active_record_store diff --git a/crowbar/change-image/dell/openstack_manager/config/locales/en.yml b/crowbar/change-image/dell/openstack_manager/config/locales/en.yml new file mode 100644 index 00000000000..d6ae408f025 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/locales/en.yml @@ -0,0 +1,255 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# + +# Sample localization file for English. Add more files in this directory for other locales. +# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. + +en: + + # Layout + nav: + dashboard: Dashboard + overview: Overview + barclamps: Barclamps + roles: Active Roles + proposals: Proposals + help: Help + + # Model Names / Attributes + model: + node: Node + proposal: Proposal + service: + name_exists: Name already exists. Please choose another. + too_short: Invalid name. Name must have some characters. + illegal_chars: Invalid name. Please limit name to [A-z0-9_] without spaces + already_commit: Proposal already in a commit + cannot_find: Failed to find proposal + attributes: + node: + name: Full Name + mac: MAC Address + allocated: Allocated + ip: IP Address + status: Status + state: State + hardware: Hardware + switch_name: Switch Name + switch_port: Switch Port + switch_name_port: Switch Name/Port + uptime: Uptime + na: Unavailable + memory: Memory + cpu: CPU + number_of_drives: Disk Drives + description: Description + roles: Roles + barclamps: Barclamps + asset_tag: Asset Tag + proposal: + name: Proposal + description: Description + status: Status + barclamp: Barclamp + data: JSON Data + role: + name: Proposal + description: Description + proposal_count: Proposals + status: Status + barclamp: Barclamp + + # Views + nodes: + index: + new_node: Add Node + title: Node list + status_empty: There are %i nodes available in the system. + status_pie: Total {0} - {1} Ready, {2} Unknown, {3} Not Ready and {4} Pending + new: + title: Add Node + show: + title: Node + ganglia: Ganglia + nagios: Nagios + bmc: IP Mgmt Interface + chef: Chef + links: Links + active: Allocated + inactive: Not Allocated + form: + allocate: Allocate + save: Save + raid: Raid + bios: BIOS + usage: Usage + save_node_success: Node saved successfully + allocate_node_success: Node allocated successfully + description: Description + barclamp: + barclamp_index: + title: Barclamps + none: None + barclamp_show: + title: Barclamp Details + create_proposal: Create Proposal + name: Name + description: Description + active: Active Proposals + proposals: All Proposals + default: default + barclamp_roles: + title: Active Roles + barclamp_proposals: + title: Proposals + proposal_index: + title: Proposals + proposal_show: + title: Edit Proposal + apply_changes: Apply changes now? + create_proposal_success: Successfully created proposal + commit_proposal: Apply Proposal + commit_proposal_success: Successfully applied proposal + commit_proposal_queued: Successfully queued proposal until the following become ready + save_proposal: Save Proposal + save_proposal_success: Successfully saved changes to proposal + delete_changes: Delete proposal now? + dequeue_proposal: Dequeue Proposal + dequeue_proposal_success: Successfully dequeued proposal + dequeue_proposal_failure: Failed to dequeued proposal + delete_proposal: Delete Proposal + delete_proposal_success: Successfully delete proposal + confirm_delete_proposal: Are you sure you wish to delete this proposal? + available_nodes: Available Nodes + ui_advice: Drag nodes from Available Nodes into desired deployment list(s). + bios_edit_attributes: + attributes: Attributes + bios_setup_enable: Enable BIOS Setup + bios_update_enable: Enable BIOS Update + debug: Enable Barclamp Debug + ipmi_edit_attributes: + attributes: Attributes + debug: Enable Barclamp Debug + bmc_enable: Enable BMC + bmc_user: BMC User + bmc_password: BMC Password + raid_edit_attributes: + attributes: Attributes + enable: Enable RAID + debug: Enable Barclamp Debug + crowbar_edit_attributes: + attributes: Attributes + ganglia_edit_attributes: + attributes: Attributes + glance_edit_attributes: + attributes: Attributes + backing_type: Backing Type + nagios_edit_attributes: + attributes: Attributes + swift_edit_attributes: + attributes: Attributes + zones: Number of Zones + partitions: Partitions + min_part_hours: Parts per Hour + replicas: Number of Replicas + cluster_hash: Cluster Hash + cluster_admin_pw: Cluster Admin Password + user: Swift User + group: Swift Group + debug: Swift Debug + crowbar_edit_deployment: + deployment: Deployment + node_selector: + title: Deployment + edit_attributes_raw: + title: Attributes + edit_deployment_raw: + title: Deployment + index: + title: Active Roles + show: + delete_role: Delete Role + confirm_delete_role: Are you sure you wish to delete this role? + delete_role_success: Successfully delete role + deployment: Nodes Activated + attributes: Proposal Attributes + role: Role + key: Key + overview: + index: + api_clients: API Clients + ops_infrastructure: Ops Infrastructure + ops_mgmt: Operations Mgmt. + nova_api: Nova API + glance_api: Glance API + swift_api: Swift API + metering: Metering + performance: Performance + monitoring: Monitoring + crowbar: Crowbar + nova: Nova + glance: Glance + swift: Swift + os: Operating System + hardware: Hardware + network: Network + + # Global + show: View + destroy: Remove + back: Go Back + edit: Edit + actions: Actions + are_you_sure: Are you sure? + unknown: Unknown + not_set: Not set + undetermined: Undetermined + name: Name + offline: Chef Offline + raw: Raw + custom: Custom + created_on: Created On + + # Error messages, etc. + error: + warning: Warning + no_nodes_found: No nodes found + + state: + unknown: Unknown + noupdate: No Data (Off) + ready: Ready + readying: Readying + install: Install + installing: Installing + installed: Installed + discovering: Discovering + discovered: Discovered + hardware-installed: Hardware Installed + hardware-installing: Hardware Installing + hardware-updated: Hardware Updated + hardware-updating: Hardware Updating + update: Hardware Update + reset: Reset + reinstall: Reinstall + allocate: Allocate + allocated: Allocated + problem: Ready Retrying + delete: Information Deleted + shutdown: Power Off + reboot: Power Restart + poweron: Power On diff --git a/crowbar/change-image/dell/openstack_manager/config/navigation.rb b/crowbar/change-image/dell/openstack_manager/config/navigation.rb new file mode 100644 index 00000000000..d86e59b733d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/navigation.rb @@ -0,0 +1,9 @@ +SimpleNavigation::Configuration.run do |navigation| + navigation.items do |primary| + primary.item :dashboard, t('nav.dashboard'), root_path + primary.item :barclamps, t('nav.barclamps'), barclamp_index_barclamp_path + primary.item :proposals, t('nav.proposals'), barclamp_proposals_barclamp_path + primary.item :roles, t('nav.roles'), barclamp_roles_barclamp_path + primary.item :help, t('nav.help'), '/users_guide.pdf' + end +end diff --git a/crowbar/change-image/dell/openstack_manager/config/routes - r3.rb b/crowbar/change-image/dell/openstack_manager/config/routes - r3.rb new file mode 100644 index 00000000000..1892beb1ab0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/routes - r3.rb @@ -0,0 +1,86 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +Crowbar::Application.routes.draw do + + resources :nodes, :only => [:index, :new] do + get 'status', :on => :collection + end + + scope 'nodes' do + constraints(:name => /.*/ ) do + match ':name/hit/:req' => "nodes#hit", :as => :hit_node + match ':name/edit' => "nodes#edit", :as => :edit_node + match ':name' => "nodes#show", :as => :node + match ':name/update' => 'nodes#update', :as => :update_node + end + match 'status' => "nodes#status", :as => :nodes_status + end + + match 'overview' => "overview#index" + + scope 'crowbar' do + version = "1.0" + + match "show/#{version}/:id", :controller => 'barclamp', :action => 'barclamp_show', :via => :get, :as => :barclamp_show_barclamp + match "roles/#{version}", :controller => 'barclamp', :action => 'barclamp_roles', :via => :get, :as => :barclamp_roles_barclamp + match "proposals/#{version}", :controller => 'barclamp', :action => 'barclamp_proposals', :via => :get, :as => :barclamp_proposals_barclamp + match "proposals/#{version}", :controller => 'barclamp', :action => 'proposal_status', :via => :get, :as => :status_proposals_barclamp + + match ":controller/#{version}/help", :action => 'help', :via => :get, :as => :help_barclamp + match ":controller/#{version}/proposals", :action => 'proposal_create', :via => :put, :as => :create_proposal_barclamp + match ":controller/#{version}/proposals", :action => 'proposals', :via => :get, :as => :proposals_barclamp + match ":controller/#{version}/proposals/commit/:id", :action => 'proposal_commit', :via => :post, :as => :commit_proposal_barclamp + match "#{version}/proposals/status.:format", :controller=>'barclamp', :action => 'proposal_status', :via => :get, :as => :status_proposals + match ":controller/#{version}/proposals/:id", :action => 'proposal_delete', :via => :delete, :as => :delete_proposal_barclamp + match ":controller/#{version}/proposals/:id", :action => 'proposal_update', :via => :post, :as => :update_proposal_barclamp + match ":controller/#{version}/proposals/:id", :action => 'proposal_show', :via => :get, :as => :proposal_barclamp + match ":controller/#{version}/elements", :action => 'elements', :via => :get + match ":controller/#{version}/elements/:id", :action => 'element_info', :via => :get + match ":controller/#{version}/transition/:id", :action => 'transition', :via => [:get, :post] + match ":controller/#{version}", :action => 'index', :via => :get, :as => :index_barclamp + match "#{version}/status", :controller => 'barclamp', :action => 'proposal_status', :via => :get, :as => :status_barclamp + match ":controller/#{version}/:id", :action => 'delete', :via => :delete, :as => :delete_barclamp + match ":controller/#{version}/:id", :action => 'show', :via => :get, :as => :show_barclamp + match ":controller/#{version}/:action/:id", :via => :post, :as => :action_barclamp + match ":controller", :action => 'versions', :via => :get, :as => :versions_barclamp + + # Generic fall through routes + match ":barclamp/#{version}/help", :action => 'help', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/proposals", :action => 'proposal_create', :via => :put, :controller => 'barclamp' + match ":barclamp/#{version}/proposals", :action => 'proposals', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/proposals/commit/:id", :action => 'proposal_commit', :via => :post, :controller => 'barclamp' + match "#{version}/proposals/status.:format", :controller => 'barclamp', :action => 'proposal_status', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/proposals/:id", :action => 'proposal_delete', :via => :delete, :controller => 'barclamp' + match ":barclamp/#{version}/proposals/:id", :action => 'proposal_update', :via => :post, :controller => 'barclamp' + match ":barclamp/#{version}/proposals/:id", :action => 'proposal_show', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/elements", :action => 'elements', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/elements/:id", :action => 'element_info', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/transition/:id", :action => 'transition', :via => [:get, :post], :controller => 'barclamp' + match ":barclamp/#{version}", :action => 'index', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/status", :action => 'status', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/:id", :action => 'delete', :via => :delete, :controller => 'barclamp' + match ":barclamp/#{version}/:id", :action => 'show', :via => :get, :controller => 'barclamp' + match ":barclamp/#{version}/:action/:id", :via => :post, :controller => 'barclamp' + match ":barclamp", :action => 'versions', :via => :get, :controller => 'barclamp' + + match "/", :controller => 'barclamp', :action => 'barclamp_index', :via => :get, :as => :barclamp_index_barclamp + end + + root :to => "nodes#index" + # match ':controller(/:action(/:id(.:format)))' + +end diff --git a/crowbar/change-image/dell/openstack_manager/config/routes.rb b/crowbar/change-image/dell/openstack_manager/config/routes.rb new file mode 100644 index 00000000000..9cb188b1d66 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/routes.rb @@ -0,0 +1,75 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +ActionController::Routing::Routes.draw do |map| + + map.root :controller => "nodes", :action=>'index' + + map.resources :nodes, :only => [:index, :new] + map.nodes_status 'nodes/status.:format', :controller => 'nodes', :action => 'status', :conditions => { :method => :get } + + map.hit_node 'nodes/:id/hit/:req', :controller=>'nodes', :action=>'hit', :constraints => { :name => /.*/ } + map.edit_node 'nodes/:name/edit', :controller=>'nodes', :action =>'edit', :constraints => { :name => /.*/ } + map.node 'nodes/:name', :controller => 'nodes', :action => 'show', :constraints => { :name => /.*/ } + map.update_node 'nodes/:name/update', :controller => 'nodes', :action=>'update', :constraints => { :name => /.*/ } + + map.overview 'overview.:format', :controller => 'overview', :action => 'index' + + map.help_barclamp 'crowbar/:controller/1.0/help', :action => 'help', :conditions => { :method => :get } + map.create_proposal_barclamp 'crowbar/:controller/1.0/proposals', :action => 'proposal_create', :conditions => { :method => :put } + map.proposals_barclamp 'crowbar/:controller/1.0/proposals', :action => 'proposals', :conditions => { :method => :get } + map.commit_proposal_barclamp 'crowbar/:controller/1.0/proposals/commit/:id', :action => 'proposal_commit', :conditions => { :method => :post } + map.status_proposals_barclamp 'crowbar/:controller/1.0/proposals/status.:format', :action => 'proposal_status', :conditions => { :method => :get } + map.delete_proposal_barclamp 'crowbar/:controller/1.0/proposals/:id', :action => 'proposal_delete', :conditions => { :method => :delete } + map.update_proposal_barclamp 'crowbar/:controller/1.0/proposals/:id', :action => 'proposal_update', :conditions => { :method => :post } + map.proposal_barclamp 'crowbar/:controller/1.0/proposals/:id', :action => 'proposal_show', :conditions => { :method => :get } + map.connect 'crowbar/:controller/1.0/elements', :action => 'elements', :conditions => { :method => :get } + map.connect 'crowbar/:controller/1.0/elements/:id', :action => 'element_info', :conditions => { :method => :get } + map.connect 'crowbar/:controller/1.0/transition/:id', :action => 'transition', :conditions => { :method => :post } + map.connect 'crowbar/:controller/1.0/transition/:id', :action => 'transition', :conditions => { :method => :get } + map.index_barclamp 'crowbar/:controller/1.0', :action => 'index', :conditions => { :method => :get } + map.status_barclamp 'crowbar/:controller/1.0/status', :action => 'status', :conditions => { :method => :get } + map.delete_barclamp 'crowbar/:controller/1.0/:id', :action => 'delete', :conditions => { :method => :delete } + map.show_barclamp 'crowbar/:controller/1.0/:id', :action => 'show', :conditions => { :method => :get } + map.versions_barclamp 'crowbar/:controller', :action => 'versions', :conditions => { :method => :get } + map.action_barclamp 'crowbar/:controller/1.0/:action/:id', :conditions => { :method => :post } + map.barclamp_index_barclamp 'crowbar', :controller => 'barclamp', :action => 'barclamp_index', :conditions => { :method => :get } + map.barclamp_show_barclamp 'crowbar/show/1.0/:id', :controller => 'barclamp', :action => 'barclamp_show', :conditions => { :method => :get } + map.barclamp_roles_barclamp 'crowbar/roles/1.0', :controller => 'barclamp', :action => 'barclamp_roles', :conditions => { :method => :get } + map.barclamp_proposals_barclamp 'crowbar/proposals/1.0', :controller => 'barclamp', :action => 'barclamp_proposals', :conditions => { :method => :get } + + map.connect 'crowbar/:barclamp/1.0/help', :action => 'help', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals', :action => 'proposal_create', :conditions => { :method => :put }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals', :action => 'proposals', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals/commit/:id', :action => 'proposal_commit', :conditions => { :method => :post }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals/status.:format', :action => 'proposal_status', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals/:id', :action => 'proposal_delete', :conditions => { :method => :delete }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals/:id', :action => 'proposal_update', :conditions => { :method => :post }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/proposals/:id', :action => 'proposal_show', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/elements', :action => 'elements', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/elements/:id', :action => 'element_info', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/transition/:id', :action => 'transition', :conditions => { :method => :post }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/transition/:id', :action => 'transition', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0', :action => 'index', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/status', :action => 'status', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/:id', :action => 'delete', :conditions => { :method => :delete }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/:id', :action => 'show', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp', :action => 'versions', :conditions => { :method => :get }, :controller => 'barclamp' + map.connect 'crowbar/:barclamp/1.0/:action/:id', :conditions => { :method => :post }, :controller => 'barclamp' + + map.connect ':controller/:action/:id' + map.connect ':controller/:action/:id.:format' +end diff --git a/crowbar/change-image/dell/openstack_manager/config/taxonomy-roles.json b/crowbar/change-image/dell/openstack_manager/config/taxonomy-roles.json new file mode 100644 index 00000000000..d7ca53c7528 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/config/taxonomy-roles.json @@ -0,0 +1,17 @@ +{ + "ntp-client":["hardware"], + "provisioner-server":["crowbar"], + "network":["network"], + "nagios-client":["monitoring"], + "nagios-server":["monitoring"], + "ganglia-client":["performance"], + "ganglia-server":["performance"], + "metering-client":["metering"], + "nova-multi-compute":["nova"], + "nova-multi-controller":["nova_api"], + "swift-storage":["swift"], + "swift-proxy":["swift_api"], + "swift-proxy-acct":["swift_api"], + "glance-server":["glance_api","glance"], + "deployer-client":["os"] +} diff --git a/crowbar/change-image/dell/openstack_manager/db/KEEP_THIS b/crowbar/change-image/dell/openstack_manager/db/KEEP_THIS new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/doc/README_FOR_APP b/crowbar/change-image/dell/openstack_manager/doc/README_FOR_APP new file mode 100644 index 00000000000..7ea130800fc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/doc/README_FOR_APP @@ -0,0 +1,16 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# diff --git a/crowbar/change-image/dell/openstack_manager/features/core_navigation.feature b/crowbar/change-image/dell/openstack_manager/features/core_navigation.feature new file mode 100644 index 00000000000..1f36804e415 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/core_navigation.feature @@ -0,0 +1,49 @@ +Feature: Check Core Navigation + In order use the system + The system operator, Oscar + wants to be able to navigate around + + Scenario: Home Page Nav + When I am on the home page + Then I should see "Dashboard" + And I should see "Barclamps" + And I should see "Active Roles" + And I should see "Proposals" + And I should see "Chef Offline" + And I should not see "Error" + + Scenario: Dashboard Nav + When I am on the home page + and I click on the "Dashboard" link + Then I should see "nodes available in the system" + And I should see "admin" + + Scenario: Barclamps Nav + When I am on the home page + and I click on the "Barclamps" link + Then I should see "Barclamps" + And I should see "crowbar" + And I should see "deployer" + And I should see "provisioner" + And I should see "dns" + And I should see "ntp" + + Scenario: Active Roles Nav + When I am on the home page + and I click on the "Active Roles" link + Then I should see "Active" + And I should see "crowbar" + And I should see "deployer" + And I should see "provisioner" + And I should see "dns" + And I should see "ntp" + + Scenario: Proposals Nav + When I am on the home page + and I click on the "Proposals" link + Then I should see "Proposals" + And I should see "crowbar" + And I should see "deployer" + And I should see "provisioner" + And I should see "dns" + And I should see "ntp" diff --git a/crowbar/change-image/dell/openstack_manager/features/manage_configs.feature b/crowbar/change-image/dell/openstack_manager/features/manage_configs.feature new file mode 100644 index 00000000000..caa3628976e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/manage_configs.feature @@ -0,0 +1,32 @@ +Feature: Manage configs + In order to change way the system installs nodes + The system operator, Oscar + wants to be able to review and set basic operating parameters + + Scenario: view base + When I am on the config page + Then I should see "Configuration" + And I should see "Crowbar" + And I should see "DNS" + And I should see "DHCP" + And I should see "Private networks" + And I should see "Public networks" + And I should see "Admin networks" + And I should see "Storage networks" + And I should see "Installer" + + Scenario: ajax view + When I am on the "dns" config ajax page + Then I should see "Domain name" + And I should see "pod.cloud.openstack.org" + And I should not see "Crowbar" + And I should not see "Home" + And I should not see "Dell" + + Scenario: change node name + Given a node address "00:00:00:00:00:00:00:00" + When I rename node "00-00-00-00-00-00-00-00" as "cuketest-rename" + And I visit the node "00-00-00-00-00-00-00-00" page + Then I should see "cuketest-rename" + And I should not see "00-00-00-00-00-00-00-00" + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/features/manage_machines.feature b/crowbar/change-image/dell/openstack_manager/features/manage_machines.feature new file mode 100644 index 00000000000..7624ffcc2f9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/manage_machines.feature @@ -0,0 +1,22 @@ +Feature: Discover & Track Machines + In order to discover the system + The machines, + wants to check in w/ the crowbar server + + Scenario: Machine registers with : MAC address + When a node registers with "01:23:45:67:89:ab:CD" + Then I should see "h01-23-45-67-89-ab-cd." + + Scenario: Machine registers with - MAC address + When a node registers with "01-23-45-67-89-ab-ef" + Then I should see "h01-23-45-67-89-ab-ef." + + Scenario: Machine registers with Name (not MAC) + When a node registers with "CUKETEST-basic" + Then I should see "cuketest-basic." + And I should not see "CUKETEST-basic." + And I should not see "hCUKETEST-basic." + + Scenario: Machine state changes + When I update node "CUKETEST-state" to "discovered" + Then the "cuketest-state" state should be "discovered" diff --git a/crowbar/change-image/dell/openstack_manager/features/manage_nodelists.feature b/crowbar/change-image/dell/openstack_manager/features/manage_nodelists.feature new file mode 100644 index 00000000000..d8b5de2fb63 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/manage_nodelists.feature @@ -0,0 +1,30 @@ +Feature: Manage nodelists + In order to view the nodes in the system + The system operator, Oscar + wants to see all the nodes and their current status + + Scenario: Node list should have an "admin" node + When I am on the nodes page + Then I should see "admin" + + Scenario: Viewing node specific information in light box + When I am on node "admin" page + Then I should see "State" + And I should see "Ready" + And I should see "Address" + And I should see "admin.pod.cloud.openstack.org" + + Scenario: Getting list of nodes + When I am on the node list page + Then the "count" entry should be at least "1" + Then the "nodes":"admin" entry should be "ready" + + Scenario: Find a specific node + Given there is a node called "CUKETEST-node-test" + When I am on the nodes page + Then I should see "cuketest-node-test" + + Scenario: Position node into rack + Given there is a node called "CUKETEST-position" + When I position "cuketest-position" in rack "test" u "30" slot "1" + Then I should find "cuketest-position" nested within "test:30:1" \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/features/setup.txt b/crowbar/change-image/dell/openstack_manager/features/setup.txt new file mode 100644 index 00000000000..afce19b577f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/setup.txt @@ -0,0 +1,24 @@ +Cucumber Required Gems: + * Cucumber + * rspec-rails + * Capybara + +Win 7 Notes: + * you must be on Gem 1.4. NOT 1.5. + * Colored Output: gem install win32console + + +To Use: + * Run all tests =cucumber= + * Run a feature file =cucumber features/manage_nodelists.feature= + * Run a scenario in a feature =cucumber features/manage_nodelists.feature:[line # of scenario]:[more scenarios]= + * verbose output: -f pretty + + +Available Capybara Session Methods; + :all, :first, :attach_file, :body, :check, :choose, :click_link_or_button, :click_button, :click_link, :current_url, :drag, :evaluate_script, + :field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?, + :has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck, + :visit, :wait_until, :within, :within_fieldset, :within_table, :within_frame, :within_window, :has_link?, :has_no_link?, :has_button?, + :has_no_button?, :has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?, + :unselect, :has_select?, :has_no_select?, :current_path, :click, :has_selector?, :has_no_selector?, :click_on diff --git a/crowbar/change-image/dell/openstack_manager/features/step_definitions/config_steps.rb b/crowbar/change-image/dell/openstack_manager/features/step_definitions/config_steps.rb new file mode 100644 index 00000000000..fe1100f4363 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/step_definitions/config_steps.rb @@ -0,0 +1,14 @@ +Given /^the following configs:$/ do |configs| + Config.create!(configs.hashes) +end + +When /^I delete the (\d+)(?:st|nd|rd|th) config$/ do |pos| + visit configs_path + within("table tr:nth-child(#{pos.to_i+1})") do + click_link "Destroy" + end +end + +Then /^I should see the following configs:$/ do |expected_configs_table| + expected_configs_table.diff!(tableish('table tr', 'td,th')) +end diff --git a/crowbar/change-image/dell/openstack_manager/features/step_definitions/machines_steps.rb b/crowbar/change-image/dell/openstack_manager/features/step_definitions/machines_steps.rb new file mode 100644 index 00000000000..75957e74fa3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/step_definitions/machines_steps.rb @@ -0,0 +1,19 @@ +require 'json' + +Given /^there is a node called "([^"]*)"$/ do |name| + visit path_to("update node \"#{name}\" to \"TESTING\"") +end + +When /^a node registers with "([^"]*)"$/ do |name| + visit path_to("update node \"#{name}\" to \"TESTING\"") +end + +When /^I update node "([^"]*)" to "([^"]*)"$/ do |name, state| + visit path_to("update node \"#{name}\" to \"#{state}\"") +end + +Then /^the "([^"]*)" state should be "([^"]*)"$/ do |name, state| + visit path_to("node list page") + result = JSON.parse(page.body) + result["nodes"][name].should == state +end diff --git a/crowbar/change-image/dell/openstack_manager/features/step_definitions/nodelist_steps.rb b/crowbar/change-image/dell/openstack_manager/features/step_definitions/nodelist_steps.rb new file mode 100644 index 00000000000..065e3feae5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/step_definitions/nodelist_steps.rb @@ -0,0 +1,11 @@ +require 'json' + +Then /^the "([^"]*)" entry should be at least "([^"]*)"$/ do |key, value| + result = JSON.parse(page.body) + assert result[key] >= value.to_i +end + +Then /^the "([^"]*)":"([^"]*)" entry should be "([^"]*)"$/ do |key1, key2, value| + result = JSON.parse(page.body) + result[key1][key2].should == value +end diff --git a/crowbar/change-image/dell/openstack_manager/features/step_definitions/web_steps.rb b/crowbar/change-image/dell/openstack_manager/features/step_definitions/web_steps.rb new file mode 100644 index 00000000000..0f0af8aa212 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/step_definitions/web_steps.rb @@ -0,0 +1,219 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + + +require 'uri' +require 'cgi' +require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths")) + +module WithinHelpers + def with_scope(locator) + locator ? within(locator) { yield } : yield + end +end +World(WithinHelpers) + +Given /^(?:|I )am on (.+)$/ do |page_name| + visit path_to(page_name) +end + +When /^(?:|I )go to (.+)$/ do |page_name| + visit path_to(page_name) +end + +When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| + with_scope(selector) do + click_button(button) + end +end + +When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| + with_scope(selector) do + click_link(link) + end +end + +When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field, value, selector| + with_scope(selector) do + fill_in(field, :with => value) + end +end + +When /^(?:|I )fill in "([^"]*)" for "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector| + with_scope(selector) do + fill_in(field, :with => value) + end +end + +# Use this to fill in an entire form with data from a table. Example: +# +# When I fill in the following: +# | Account Number | 5002 | +# | Expiry date | 2009-11-01 | +# | Note | Nice guy | +# | Wants Email? | | +# +# TODO: Add support for checkbox, select og option +# based on naming conventions. +# +When /^(?:|I )fill in the following(?: within "([^"]*)")?:$/ do |selector, fields| + with_scope(selector) do + fields.rows_hash.each do |name, value| + When %{I fill in "#{name}" with "#{value}"} + end + end +end + +When /^(?:|I )select "([^"]*)" from "([^"]*)"(?: within "([^"]*)")?$/ do |value, field, selector| + with_scope(selector) do + select(value, :from => field) + end +end + +When /^(?:|I )check "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| + with_scope(selector) do + check(field) + end +end + +When /^(?:|I )uncheck "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| + with_scope(selector) do + uncheck(field) + end +end + +When /^(?:|I )choose "([^"]*)"(?: within "([^"]*)")?$/ do |field, selector| + with_scope(selector) do + choose(field) + end +end + +When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"(?: within "([^"]*)")?$/ do |path, field, selector| + with_scope(selector) do + attach_file(field, path) + end +end + +Then /^(?:|I )should see JSON:$/ do |expected_json| + require 'json' + expected = JSON.pretty_generate(JSON.parse(expected_json)) + actual = JSON.pretty_generate(JSON.parse(response.body)) + expected.should == actual +end + +Then /^(?:|I )should see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector| + with_scope(selector) do + if page.respond_to? :should + page.should have_content(text) + else + assert page.has_content?(text) + end + end +end + +Then /^(?:|I )should see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector| + regexp = Regexp.new(regexp) + with_scope(selector) do + if page.respond_to? :should + page.should have_xpath('//*', :text => regexp) + else + assert page.has_xpath?('//*', :text => regexp) + end + end +end + +Then /^(?:|I )should not see "([^"]*)"(?: within "([^"]*)")?$/ do |text, selector| + with_scope(selector) do + if page.respond_to? :should + page.should have_no_content(text) + else + assert page.has_no_content?(text) + end + end +end + +Then /^(?:|I )should not see \/([^\/]*)\/(?: within "([^"]*)")?$/ do |regexp, selector| + regexp = Regexp.new(regexp) + with_scope(selector) do + if page.respond_to? :should + page.should have_no_xpath('//*', :text => regexp) + else + assert page.has_no_xpath?('//*', :text => regexp) + end + end +end + +Then /^the "([^"]*)" field(?: within "([^"]*)")? should contain "([^"]*)"$/ do |field, selector, value| + with_scope(selector) do + field = find_field(field) + field_value = (field.tag_name == 'textarea') ? field.text : field.value + if field_value.respond_to? :should + field_value.should =~ /#{value}/ + else + assert_match(/#{value}/, field_value) + end + end +end + +Then /^the "([^"]*)" field(?: within "([^"]*)")? should not contain "([^"]*)"$/ do |field, selector, value| + with_scope(selector) do + field = find_field(field) + field_value = (field.tag_name == 'textarea') ? field.text : field.value + if field_value.respond_to? :should_not + field_value.should_not =~ /#{value}/ + else + assert_no_match(/#{value}/, field_value) + end + end +end + +Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should be checked$/ do |label, selector| + with_scope(selector) do + field_checked = find_field(label)['checked'] + if field_checked.respond_to? :should + field_checked.should be_true + else + assert field_checked + end + end +end + +Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |label, selector| + with_scope(selector) do + field_checked = find_field(label)['checked'] + if field_checked.respond_to? :should + field_checked.should be_false + else + assert !field_checked + end + end +end + +Then /^(?:|I )should be on (.+)$/ do |page_name| + current_path = URI.parse(current_url).path + if current_path.respond_to? :should + current_path.should == path_to(page_name) + else + assert_equal path_to(page_name), current_path + end +end + +Then /^(?:|I )should have the following query string:$/ do |expected_pairs| + query = URI.parse(current_url).query + actual_params = query ? CGI.parse(query) : {} + expected_params = {} + expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')} + + if actual_params.respond_to? :should + actual_params.should == expected_params + else + assert_equal expected_params, actual_params + end +end + +Then /^show me the page$/ do + save_and_open_page +end diff --git a/crowbar/change-image/dell/openstack_manager/features/support/env.rb b/crowbar/change-image/dell/openstack_manager/features/support/env.rb new file mode 100644 index 00000000000..77e1701f90c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/support/env.rb @@ -0,0 +1,58 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + +ENV["RAILS_ENV"] ||= "cucumber" +require File.expand_path(File.dirname(__FILE__) + '/../../config/environment') + +require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support +require 'cucumber/rails/world' +require 'cucumber/rails/active_record' +require 'cucumber/web/tableish' + +require 'capybara/rails' +require 'capybara/cucumber' +require 'capybara/session' +require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links with onclick javascript handlers without using @culerity or @javascript +# Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In +# order to ease the transition to Capybara we set the default here. If you'd +# prefer to use XPath just remove this line and adjust any selectors in your +# steps to use the XPath syntax. +Capybara.default_selector = :css + +# If you set this to false, any error raised from within your app will bubble +# up to your step definition and out to cucumber unless you catch it somewhere +# on the way. You can make Rails rescue errors and render error pages on a +# per-scenario basis by tagging a scenario or feature with the @allow-rescue tag. +# +# If you set this to true, Rails will rescue all errors and render error +# pages, more or less in the same way your application would behave in the +# default production environment. It's not recommended to do this for all +# of your scenarios, as this makes it hard to discover errors in your application. +ActionController::Base.allow_rescue = false + +# If you set this to true, each scenario will run in a database transaction. +# You can still turn off transactions on a per-scenario basis, simply tagging +# a feature or scenario with the @no-txn tag. If you are using Capybara, +# tagging with @culerity or @javascript will also turn transactions off. +# +# If you set this to false, transactions will be off for all scenarios, +# regardless of whether you use @no-txn or not. +# +# Beware that turning transactions off will leave data in your database +# after each scenario, which can lead to hard-to-debug failures in +# subsequent scenarios. If you do this, we recommend you create a Before +# block that will explicitly put your database in a known state. +Cucumber::Rails::World.use_transactional_fixtures = false + +# How to clean your database when transactions are turned off. See +# http://github.com/bmabey/database_cleaner for more info. +if defined?(ActiveRecord::Base) + begin + require 'database_cleaner' + DatabaseCleaner.strategy = :truncation + rescue LoadError => ignore_if_database_cleaner_not_present + end +end diff --git a/crowbar/change-image/dell/openstack_manager/features/support/paths.rb b/crowbar/change-image/dell/openstack_manager/features/support/paths.rb new file mode 100644 index 00000000000..cf857fd1151 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/features/support/paths.rb @@ -0,0 +1,51 @@ +module NavigationHelpers + # Maps a name to a path. Used by the + # + # When /^I go to (.+)$/ do |page_name| + # + # step definition in web_steps.rb + # + def path_to(page_name) + case page_name + + when /the home\s?page/ + '/' + + when /the config page/ + 'config/index' + + when /the nodes page/ + 'main/nodes' + + when /node "(.*)" page/ + 'main/nodebox/'+$1 + + when /node list page/ + 'main/nodelist' + + when /the "(.*)" config ajax page/ + 'config/index/'+$1+'?ajax=true' + + when /update node "(.*)" to "(.*)"/ + "machines/state/#{$1}/#{$2}" + + # Add more mappings here. + # Here is an example that pulls values out of the Regexp: + # + # when /^(.*)'s profile page$/i + # user_profile_path(User.find_by_login($1)) + + else + begin + page_name =~ /the (.*) page/ + path_components = $1.split(/\s+/) + self.send(path_components.push('path').join('_').to_sym) + rescue Object => e + raise "Can't find mapping from \"#{page_name}\" to a path.\n" + + "Now, go and add a mapping in #{__FILE__}" + end + end + end +end + +World(NavigationHelpers) diff --git a/crowbar/change-image/dell/openstack_manager/lib/crowbar_offline.rb b/crowbar/change-image/dell/openstack_manager/lib/crowbar_offline.rb new file mode 100644 index 00000000000..868ee56476b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/lib/crowbar_offline.rb @@ -0,0 +1,97 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: RobHirschfeld +# +module CrowbarOffline +#/lib/crowbar_offline.rb + + OFFLINE_FILES_DIR = 'db' + + def nfile(serial, name) + n = if name.nil? + "#{serial}.index" + elsif serial.nil? + "#{name}.json" + else + "#{serial}-#{name}.json" + end + File.join(OFFLINE_FILES_DIR, n) + end + + # returns list of files that match the criteria + def offline_search(serial, search) + files = Dir.entries(OFFLINE_FILES_DIR).find_all { |f| /^#{serial}#{search}(.*).json$/ =~ f } + files.map! { |f| File.join(OFFLINE_FILES_DIR, f) } + tfiles = Dir.entries("tmp").find_all { |f| /^#{serial}#{search}(.*).json$/ =~ f } + tfiles.map! { |f| File.join(OFFLINE_FILES_DIR, f) } + (tfiles+files).uniq + end + + def dump(o, serial, name) + file = nfile(serial, name) + unless File.exist? file + begin + File.open(file, 'w') do |f| + f.puts o.to_json + end + Rails.logger.info("Dumped Chef #{serial} #{name} object to '#{file}'") + rescue Exception => e + Rails.logger.warn("Error dumping Chef #{serial} object to '#{name}' with '#{e.inspect}'") + end #dump object + end #unless exist + end + + def offline_cache(o, file) + cache = File.join('tmp', file[/#{OFFLINE_FILES_DIR}[\/\\](.*)/,1]) + File.open(cache, 'w') { |f| f.puts o.to_json } + Rails.logger.debug "Cached #{o} to #{cache}" + end + + def clear_cache(o) + file = case o.class.to_s + when 'Chef::Node' + "node-#{o.name}.json" + when 'Chef::Role' + "role-#{o.name}.json" + when 'Chef::DataBagItem' + "data-bag-item_#{o.name}.json" + else + throw "Crowbar Offline Clear Cache missing class response for '#{o.class.to_s}'" + end + file = File.join('tmp', file) + Rails.logger.info("Removed Cached Object '#{file}'") + puts "Cache delete #{file}" + File.delete(file) if File.exists? file + end + + def recover_json(file) + cache = File.join('tmp', file[/#{OFFLINE_FILES_DIR}[\/\\](.*)/,1]) + if File.exists? cache + Rails.logger.debug("Cache hit for #{cache}") + file = cache + end + begin + if File.exist? file + return JSON::load File.open("#{file}", 'r') + else + Rails.logger.warn("Error file not found for chef object. Looking for '#{file}'") + end + rescue Exception => e + Rails.logger.error("Error recovering chef object from '#{file}' caused by error '#{e.inspect}'") + end + return nil + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/lib/tasks/cucumber.rake b/crowbar/change-image/dell/openstack_manager/lib/tasks/cucumber.rake new file mode 100644 index 00000000000..7db1a55704f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/lib/tasks/cucumber.rake @@ -0,0 +1,53 @@ +# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. +# It is recommended to regenerate this file in the future when you upgrade to a +# newer version of cucumber-rails. Consider adding your own code to a new file +# instead of editing this one. Cucumber will automatically load all features/**/*.rb +# files. + + +unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks + +vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first +$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? + +begin + require 'cucumber/rake/task' + + namespace :cucumber do + Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| + t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. + t.fork = true # You may get faster startup if you set this to false + t.profile = 'default' + end + + Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'wip' + end + + Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t| + t.binary = vendored_cucumber_bin + t.fork = true # You may get faster startup if you set this to false + t.profile = 'rerun' + end + + desc 'Run all features' + task :all => [:ok, :wip] + end + desc 'Alias for cucumber:ok' + task :cucumber => 'cucumber:ok' + + task :default => :cucumber + + task :features => :cucumber do + STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" + end +rescue LoadError + desc 'cucumber rake task not available (cucumber not installed)' + task :cucumber do + abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' + end +end + +end diff --git a/crowbar/change-image/dell/openstack_manager/log/KEEP_ME b/crowbar/change-image/dell/openstack_manager/log/KEEP_ME new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/public/404.html b/crowbar/change-image/dell/openstack_manager/public/404.html new file mode 100644 index 00000000000..eff660b90c3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/404.html @@ -0,0 +1,30 @@ + + + + + + + The page you were looking for doesn't exist (404) + + + + + +
    +

    The page you were looking for doesn't exist.

    +

    You may have mistyped the address or the page may have moved.

    +
    + + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/public/422.html b/crowbar/change-image/dell/openstack_manager/public/422.html new file mode 100644 index 00000000000..b54e4a3cadb --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/422.html @@ -0,0 +1,30 @@ + + + + + + + The change you wanted was rejected (422) + + + + + +
    +

    The change you wanted was rejected.

    +

    Maybe you tried to change something you didn't have access to.

    +
    + + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/public/500.html b/crowbar/change-image/dell/openstack_manager/public/500.html new file mode 100644 index 00000000000..ec3bbf02c43 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/500.html @@ -0,0 +1,30 @@ + + + + + + + We're sorry, but something went wrong (500) + + + + + +
    +

    We're sorry, but something went wrong.

    +

    We've been notified about this issue and we'll take a look at it shortly.

    +
    + + diff --git a/crowbar/change-image/dell/openstack_manager/public/favicon.ico b/crowbar/change-image/dell/openstack_manager/public/favicon.ico new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/public/images/bunny.png b/crowbar/change-image/dell/openstack_manager/public/images/bunny.png new file mode 100644 index 00000000000..00ca6e62139 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/bunny.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/controls.png b/crowbar/change-image/dell/openstack_manager/public/images/controls.png new file mode 100644 index 00000000000..9257176616c Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/controls.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/crowbar-logo.png b/crowbar/change-image/dell/openstack_manager/public/images/crowbar-logo.png new file mode 100644 index 00000000000..718cb9e4ab3 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/crowbar-logo.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/crowbar.png b/crowbar/change-image/dell/openstack_manager/public/images/crowbar.png new file mode 100644 index 00000000000..2a3a0436ce0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/images/crowbar.png @@ -0,0 +1,936 @@ + + + + + + + + + + + + public/images/layout/crowbar.png at master from dellcloudedge/crowbar - GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    Markdown Cheat Sheet

    + +
    + +
    +
    +

    Format Text

    +

    Headers

    +
    +# This is an <h1> tag
    +## This is an <h2> tag
    +###### This is an <h6> tag
    +

    Text styles

    +
    +*This text will be italic*
    +_This will also be italic_
    +**This text will be bold**
    +__This will also be bold__
    +
    +*You **can** combine them*
    +
    +
    +
    +

    Lists

    +

    Unordered

    +
    +* Item 1
    +* Item 2
    +  * Item 2a
    +  * Item 2b
    +

    Ordered

    +
    +1. Item 1
    +2. Item 2
    +3. Item 3
    +   * Item 3a
    +   * Item 3b
    +
    +
    +

    Miscellaneous

    +

    Images

    +
    +![GitHub Logo](/images/logo.png)
    +Format: ![Alt Text](url)
    +
    +

    Links

    +
    +http://github.com - automatic!
    +[GitHub](http://github.com)
    +

    Blockquotes

    +
    +As Kanye West said:
    +> We're living the future so
    +> the present is our past.
    +
    +
    +
    +
    + +

    Code Examples in Markdown

    +
    +

    Syntax highlighting with GFM

    +
    +```javascript
    +function fancyAlert(arg) {
    +  if(arg) {
    +    $.facebox({div:'#foo'})
    +  }
    +}
    +```
    +
    +
    +

    Or, indent your code 4 spaces

    +
    +Here is a Python code example
    +without syntax highlighting:
    +
    +    def foo:
    +      if not bar:
    +        return true
    +
    +
    +

    Inline code for comments

    +
    +I think you should use an
    +`<addr>` element here instead.
    +
    +
    + +
    + + + + + + + + + + + + + + diff --git a/crowbar/change-image/dell/openstack_manager/public/images/dell_blue_logo.jpg b/crowbar/change-image/dell/openstack_manager/public/images/dell_blue_logo.jpg new file mode 100644 index 00000000000..004e41a09b0 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/dell_blue_logo.jpg differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/dell_tagline_logo.jpg b/crowbar/change-image/dell/openstack_manager/public/images/dell_tagline_logo.jpg new file mode 100644 index 00000000000..4bc9275ea51 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/dell_tagline_logo.jpg differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/icons/ajax-loader.gif b/crowbar/change-image/dell/openstack_manager/public/images/icons/ajax-loader.gif new file mode 100644 index 00000000000..62697d31db2 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/icons/ajax-loader.gif differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/icons/led/green.png b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/green.png new file mode 100644 index 00000000000..1fedaef7949 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/green.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/icons/led/off.png b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/off.png new file mode 100644 index 00000000000..79ce80a5cc5 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/off.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/icons/led/red.png b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/red.png new file mode 100644 index 00000000000..4694f62a336 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/red.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/icons/led/yellow.png b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/yellow.png new file mode 100644 index 00000000000..7de65d8005c Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/icons/led/yellow.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomCenter.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomCenter.png new file mode 100644 index 00000000000..0d4475edf4e Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomCenter.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomLeft.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomLeft.png new file mode 100644 index 00000000000..2775eba899d Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomLeft.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomRight.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomRight.png new file mode 100644 index 00000000000..f7f51379c08 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderBottomRight.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderMiddleLeft.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderMiddleLeft.png new file mode 100644 index 00000000000..a2d63d156e0 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderMiddleLeft.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderMiddleRight.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderMiddleRight.png new file mode 100644 index 00000000000..fd7c3e84913 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderMiddleRight.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopCenter.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopCenter.png new file mode 100644 index 00000000000..2937a9cf9e2 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopCenter.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopLeft.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopLeft.png new file mode 100644 index 00000000000..f9d458b5bba Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopLeft.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopRight.png b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopRight.png new file mode 100644 index 00000000000..74b8583cfbb Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/internet_explorer/borderTopRight.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/layout/bunny.png b/crowbar/change-image/dell/openstack_manager/public/images/layout/bunny.png new file mode 100644 index 00000000000..edd9709dc9c Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/layout/bunny.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/layout/crowbar.png b/crowbar/change-image/dell/openstack_manager/public/images/layout/crowbar.png new file mode 100644 index 00000000000..714f9c63521 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/layout/crowbar.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/layout/dell.png b/crowbar/change-image/dell/openstack_manager/public/images/layout/dell.png new file mode 100644 index 00000000000..410e7340df9 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/layout/dell.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/loading.gif b/crowbar/change-image/dell/openstack_manager/public/images/loading.gif new file mode 100644 index 00000000000..b4695d811d4 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/loading.gif differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/loading_background.png b/crowbar/change-image/dell/openstack_manager/public/images/loading_background.png new file mode 100644 index 00000000000..6ae83e69735 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/loading_background.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/mesa.png b/crowbar/change-image/dell/openstack_manager/public/images/mesa.png new file mode 100644 index 00000000000..40854aaf04f Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/mesa.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/open-stack-logo.png b/crowbar/change-image/dell/openstack_manager/public/images/open-stack-logo.png new file mode 100644 index 00000000000..146faec5cfe Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/open-stack-logo.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/images/overlay.png b/crowbar/change-image/dell/openstack_manager/public/images/overlay.png new file mode 100644 index 00000000000..53ea98f7003 Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/public/images/overlay.png differ diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/application.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/application.js new file mode 100644 index 00000000000..17addfbddfd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/application.js @@ -0,0 +1,73 @@ +(function(a){a.fn.konami=function(b,c){c=a.extend({},a.fn.konami.params,c);this.each(function(){var d=a(this);d.bind("konami",b).bind("keyup",function(e){a.fn.konami.checkCode(e,c,d);});});return this;};a.fn.konami.params={code:[38,38,40,40,37,39,37,39,66,65],step:0};a.fn.konami.checkCode=function(b,c,d){if(b.keyCode==c.code[c.step]){c.step++;}else{c.step=0;}if(c.step==c.code.length){d.trigger("konami");c.step=0;}};})(jQuery); +var piechart_options = {type: 'pie', width: '17px', height: '17px', sliceColors: ["#0f0", "#f00", "#999", "#ff0"] }; + +jQuery(document).ready(function($) { + + $('textarea.editor').each(function(index) { + CodeMirror.fromTextArea(this, { + lineNumbers: true, + matchBrackets: true + }); + }); + + $('a.node_details').click(function(e) { + $('#details').load($(this).attr('href')); + $('tr.selected').removeClass('selected'); + $(this).parents('tr').addClass('selected'); + e.preventDefault(); + }); + + $('#details .roles a').live('click', function(e) { + link = $(this); + $.getJSON(link.attr('href'), function(data) { + $('a.highlight').removeClass('highlight'); + link.addClass('highlight'); + $('tr.selected').removeClass('selected'); + $.each(data['nodes'], function(i,node) { + $('tr#'+node).addClass('selected'); + }); + }); + e.preventDefault(); + }); + + $('#details .barclamps a').live('click', function(e) { + link = $(this); + $.getJSON(link.attr('href'), function(data) { + $('a.highlight').removeClass('highlight'); + link.addClass('highlight'); + $('tr.selected').removeClass('selected'); + $.each(data['nodes'], function(i,node) { + $('tr#'+node).addClass('selected'); + }); + }); + e.preventDefault(); + }); + + $('.inline_piechart').sparkline('html', piechart_options ); + + setInterval( function() { + $('.led.unready, .led.pending').toggleClass('blink'); + }, 500); + + if(typeof update == 'function') { + setInterval("update()", 10000); + } + + $('.button').live('click', function() { + var button = $(this); + button.addClass('pressed'); + if(button.attr('data-remote')=='true') { + button.bind('ajax:complete', function(){ button.removeClass('pressed'); }); + } + }); + + $('input[data-default]').each(function() { + $(this).val($(this).attr('data-default')).addClass('default'); + }).click(function(e){ + $(this).val('').removeClass('default'); + }); + + $(document).konami(function(){ + $("header h1 a").css('background-image','url("/images/layout/bunny.png")').css('width','279px'); + }); +}); diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/codemirror/codemirror.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/codemirror/codemirror.js new file mode 100644 index 00000000000..698a9a35520 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/codemirror/codemirror.js @@ -0,0 +1,2137 @@ +// All functions that need access to the editor's state live inside +// the CodeMirror function. Below that, at the bottom of the file, +// some utilities are defined. + +// CodeMirror is the only global var we claim +var CodeMirror = (function() { + // This is the function that produces an editor instance. It's + // closure is used to store the editor state. + function CodeMirror(place, givenOptions) { + // Determine effective options based on given values and defaults. + var options = {}, defaults = CodeMirror.defaults; + for (var opt in defaults) + if (defaults.hasOwnProperty(opt)) + options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + + var targetDocument = options["document"]; + // The element in which the editor lives. + var wrapper = targetDocument.createElement("div"); + wrapper.className = "CodeMirror"; + // This mess creates the base DOM structure for the editor. + wrapper.innerHTML = + '
    ' + // Wraps and hides input textarea + '
    ' + + '
    ' + + '
    ' + // Set to the height of the text, causes scrolling + '
    ' + + '
    ' + // Moved around its parent to cover visible view + '
    ' + + // Provides positioning relative to (visible) text origin + '
    ' + + '
     
    ' + // Absolutely positioned blinky cursor + '
    ' + // This DIV contains the actual code + '
    '; + if (place.appendChild) place.appendChild(wrapper); else place(wrapper); + // I've never seen more elegant code in my life. + var inputDiv = wrapper.firstChild, input = inputDiv.firstChild, + scroller = wrapper.lastChild, code = scroller.firstChild, + measure = code.firstChild, mover = measure.nextSibling, + gutter = mover.firstChild, gutterText = gutter.firstChild, + lineSpace = gutter.nextSibling.firstChild, + cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling; + if (options.tabindex != null) input.tabindex = options.tabindex; + if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; + + // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. + var poll = new Delayed(), highlight = new Delayed(), blinker; + + // mode holds a mode API object. lines an array of Line objects + // (see Line constructor), work an array of lines that should be + // parsed, and history the undo history (instance of History + // constructor). + var mode, lines = [new Line("")], work, history = new History(), focused; + loadMode(); + // The selection. These are always maintained to point at valid + // positions. Inverted is used to remember that the user is + // selecting bottom-to-top. + var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; + // Selection-related flags. shiftSelecting obviously tracks + // whether the user is holding shift. reducedSelection is a hack + // to get around the fact that we can't create inverted + // selections. See below. + var shiftSelecting, reducedSelection, lastDoubleClick; + // Variables used by startOperation/endOperation to track what + // happened during the operation. + var updateInput, changes, textChanged, selectionChanged, leaveInputAlone; + // Current visible range (may be bigger than the view window). + var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null; + // editing will hold an object describing the things we put in the + // textarea, to help figure out whether something changed. + // bracketHighlighted is used to remember that a backet has been + // marked. + var editing, bracketHighlighted; + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + var maxLine = ""; + + // Initialize the content. + operation(function(){setValue(options.value || ""); updateInput = false;})(); + + // Register our event handlers. + connect(scroller, "mousedown", operation(onMouseDown)); + // Gecko browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for Gecko. + if (!gecko) connect(scroller, "contextmenu", onContextMenu); + connect(code, "dblclick", operation(onDblClick)); + connect(scroller, "scroll", function() {updateDisplay([]); if (options.onScroll) options.onScroll(instance);}); + connect(window, "resize", function() {updateDisplay(true);}); + connect(input, "keyup", operation(onKeyUp)); + connect(input, "keydown", operation(onKeyDown)); + connect(input, "keypress", operation(onKeyPress)); + connect(input, "focus", onFocus); + connect(input, "blur", onBlur); + + connect(scroller, "dragenter", function(e){e.stop();}); + connect(scroller, "dragover", function(e){e.stop();}); + connect(scroller, "drop", operation(onDrop)); + connect(scroller, "paste", function(){focusInput(); fastPoll();}); + connect(input, "paste", function(){fastPoll();}); + connect(input, "cut", function(){fastPoll();}); + + // IE throws unspecified error in certain cases, when + // trying to access activeElement before onload + var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { } + if (hasFocus) setTimeout(onFocus, 20); + else onBlur(); + + function isLine(l) {return l >= 0 && l < lines.length;} + // The instance object that we'll return. Mostly calls out to + // local functions in the CodeMirror function. Some do some extra + // range checking and/or clipping. operation is used to wrap the + // call so that changes it makes are tracked, and the display is + // updated afterwards. + var instance = { + getValue: getValue, + setValue: operation(setValue), + getSelection: getSelection, + replaceSelection: operation(replaceSelection), + focus: function(){focusInput(); onFocus(); fastPoll();}, + setOption: function(option, value) { + options[option] = value; + if (option == "lineNumbers" || option == "gutter") gutterChanged(); + else if (option == "mode" || option == "indentUnit") loadMode(); + else if (option == "readOnly" && value == "nocursor") input.blur(); + else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value); + }, + getOption: function(option) {return options[option];}, + undo: operation(undo), + redo: operation(redo), + indentLine: operation(function(n) {if (isLine(n)) indentLine(n, "smart");}), + historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, + matchBrackets: operation(function(){matchBrackets(true);}), + getTokenAt: function(pos) { + pos = clipPos(pos); + return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch); + }, + getStateAfter: function(line) { + line = clipLine(line == null ? lines.length - 1: line); + return getStateBefore(line + 1); + }, + cursorCoords: function(start){ + if (start == null) start = sel.inverted; + return pageCoords(start ? sel.from : sel.to); + }, + charCoords: function(pos){return pageCoords(clipPos(pos));}, + coordsChar: function(coords) { + var off = eltOffset(lineSpace); + var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight()))); + return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)}); + }, + getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);}, + markText: operation(function(a, b, c){return operation(markText(a, b, c));}), + setMarker: addGutterMarker, + clearMarker: removeGutterMarker, + setLineClass: operation(setLineClass), + lineInfo: lineInfo, + addWidget: function(pos, node, scroll, where) { + pos = localCoords(clipPos(pos)); + var top = pos.yBot, left = pos.x; + node.style.position = "absolute"; + code.appendChild(node); + node.style.left = left + "px"; + if (where == "over") top = pos.y; + else if (where == "near") { + var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()), + hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft(); + if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) + top = pos.y - node.offsetHeight; + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth; + } + node.style.top = (top + paddingTop()) + "px"; + node.style.left = (left + paddingLeft()) + "px"; + if (scroll) + scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); + }, + + lineCount: function() {return lines.length;}, + getCursor: function(start) { + if (start == null) start = sel.inverted; + return copyPos(start ? sel.from : sel.to); + }, + somethingSelected: function() {return !posEq(sel.from, sel.to);}, + setCursor: operation(function(line, ch) { + if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch); + else setCursor(line, ch); + }), + setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}), + getLine: function(line) {if (isLine(line)) return lines[line].text;}, + setLine: operation(function(line, text) { + if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: lines[line].text.length}); + }), + removeLine: operation(function(line) { + if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); + }), + replaceRange: operation(replaceRange), + getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));}, + + operation: function(f){return operation(f)();}, + refresh: function(){updateDisplay(true);}, + getInputField: function(){return input;}, + getWrapperElement: function(){return wrapper;}, + getScrollerElement: function(){return scroller;} + }; + + function setValue(code) { + history = null; + var top = {line: 0, ch: 0}; + updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length}, + splitLines(code), top, top); + history = new History(); + } + function getValue(code) { + var text = []; + for (var i = 0, l = lines.length; i < l; ++i) + text.push(lines[i].text); + return text.join("\n"); + } + + function onMouseDown(e) { + var ld = lastDoubleClick; lastDoubleClick = null; + // First, see if this is a click in the gutter + for (var n = e.target(); n != wrapper; n = n.parentNode) + if (n.parentNode == gutterText) { + if (options.onGutterClick) + options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom); + return e.stop(); + } + + var start = posFromMouse(e); + switch (e.button()) { + case 3: + if (gecko && !mac) onContextMenu(e); + return; + case 2: + if (start) setCursor(start.line, start.ch, true); + return; + } + // For button 1, if it was clicked inside the editor + // (posFromMouse returning non-null), we have to adjust the + // selection. + if (!start) {if (e.target() == scroller) e.stop(); return;} + + if (!focused) onFocus(); + e.stop(); + if (ld && +new Date - ld < 400) return selectLine(start.line); + + setCursor(start.line, start.ch, true); + var last = start, going; + // And then we have to see if it's a drag event, in which case + // the dragged-over text must be selected. + function end() { + focusInput(); + updateInput = true; + move(); up(); + } + function extend(e) { + var cur = posFromMouse(e, true); + if (cur && !posEq(cur, last)) { + if (!focused) onFocus(); + last = cur; + setSelectionUser(start, cur); + updateInput = false; + var visible = visibleLines(); + if (cur.line >= visible.to || cur.line < visible.from) + going = setTimeout(operation(function(){extend(e);}), 150); + } + } + + var move = connect(targetDocument, "mousemove", operation(function(e) { + clearTimeout(going); + e.stop(); + extend(e); + }), true); + var up = connect(targetDocument, "mouseup", operation(function(e) { + clearTimeout(going); + var cur = posFromMouse(e); + if (cur) setSelectionUser(start, cur); + e.stop(); + end(); + }), true); + } + function onDblClick(e) { + var pos = posFromMouse(e); + if (!pos) return; + selectWordAt(pos); + e.stop(); + lastDoubleClick = +new Date; + } + function onDrop(e) { + e.e.preventDefault(); + var pos = posFromMouse(e, true), files = e.e.dataTransfer.files; + if (!pos || options.readOnly) return; + if (files && files.length && window.FileReader && window.File) { + function loadFile(file, i) { + var reader = new FileReader; + reader.onload = function() { + text[i] = reader.result; + if (++read == n) replaceRange(text.join(""), clipPos(pos), clipPos(pos)); + }; + reader.readAsText(file); + } + var n = files.length, text = Array(n), read = 0; + for (var i = 0; i < n; ++i) loadFile(files[i], i); + } + else { + try { + var text = e.e.dataTransfer.getData("Text"); + if (text) replaceRange(text, pos, pos); + } + catch(e){} + } + } + function onKeyDown(e) { + if (!focused) onFocus(); + + var code = e.e.keyCode; + // IE does strange things with escape. + if (ie && code == 27) { e.e.returnValue = false; } + // Tries to detect ctrl on non-mac, cmd on mac. + var mod = (mac ? e.e.metaKey : e.e.ctrlKey) && !e.e.altKey, anyMod = e.e.ctrlKey || e.e.altKey || e.e.metaKey; + if (code == 16 || e.e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); + else shiftSelecting = null; + // First give onKeyEvent option a chance to handle this. + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return; + + if (code == 33 || code == 34) {scrollPage(code == 34); return e.stop();} // page up/down + if (mod && ((code == 36 || code == 35) || // ctrl-home/end + mac && (code == 38 || code == 40))) { // cmd-up/down + scrollEnd(code == 36 || code == 38); return e.stop(); + } + if (mod && code == 65) {selectAll(); return e.stop();} // ctrl-a + if (!options.readOnly) { + if (!anyMod && code == 13) {return;} // enter + if (!anyMod && code == 9 && handleTab(e.e.shiftKey)) return e.stop(); // tab + if (mod && code == 90) {undo(); return e.stop();} // ctrl-z + if (mod && ((e.e.shiftKey && code == 90) || code == 89)) {redo(); return e.stop();} // ctrl-shift-z, ctrl-y + } + + // Key id to use in the movementKeys map. We also pass it to + // fastPoll in order to 'self learn'. We need this because + // reducedSelection, the hack where we collapse the selection to + // its start when it is inverted and a movement key is pressed + // (and later restore it again), shouldn't be used for + // non-movement keys. + curKeyId = (mod ? "c" : "") + code; + if (sel.inverted && movementKeys.hasOwnProperty(curKeyId)) { + var range = selRange(input); + if (range) { + reducedSelection = {anchor: range.start}; + setSelRange(input, range.start, range.start); + } + } + fastPoll(curKeyId); + } + function onKeyUp(e) { + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return; + if (reducedSelection) { + reducedSelection = null; + updateInput = true; + } + if (e.e.keyCode == 16) shiftSelecting = null; + } + function onKeyPress(e) { + if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return; + if (options.electricChars && mode.electricChars) { + var ch = String.fromCharCode(e.e.charCode == null ? e.e.keyCode : e.e.charCode); + if (mode.electricChars.indexOf(ch) > -1) + setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50); + } + var code = e.e.keyCode; + // Re-stop tab and enter. Necessary on some browsers. + if (code == 13) {if (!options.readOnly) handleEnter(); e.stop();} + else if (!e.e.ctrlKey && !e.e.altKey && !e.e.metaKey && code == 9 && options.tabMode != "default") e.stop(); + else fastPoll(curKeyId); + } + + function onFocus() { + if (options.readOnly == "nocursor") return; + if (!focused) { + if (options.onFocus) options.onFocus(instance); + focused = true; + if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1) + wrapper.className += " CodeMirror-focused"; + if (!leaveInputAlone) prepareInput(); + } + slowPoll(); + restartBlink(); + } + function onBlur() { + if (focused) { + if (options.onBlur) options.onBlur(instance); + focused = false; + wrapper.className = wrapper.className.replace(" CodeMirror-focused", ""); + } + clearInterval(blinker); + setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); + } + + // Replace the range from from to to by the strings in newText. + // Afterwards, set the selection to selFrom, selTo. + function updateLines(from, to, newText, selFrom, selTo) { + if (history) { + var old = []; + for (var i = from.line, e = to.line + 1; i < e; ++i) old.push(lines[i].text); + history.addChange(from.line, newText.length, old); + while (history.done.length > options.undoDepth) history.done.shift(); + } + updateLinesNoUndo(from, to, newText, selFrom, selTo); + } + function unredoHelper(from, to) { + var change = from.pop(); + if (change) { + var replaced = [], end = change.start + change.added; + for (var i = change.start; i < end; ++i) replaced.push(lines[i].text); + to.push({start: change.start, added: change.old.length, old: replaced}); + var pos = clipPos({line: change.start + change.old.length - 1, + ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}); + updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos); + } + } + function undo() {unredoHelper(history.done, history.undone);} + function redo() {unredoHelper(history.undone, history.done);} + + function updateLinesNoUndo(from, to, newText, selFrom, selTo) { + var recomputeMaxLength = false, maxLineLength = maxLine.length; + for (var i = from.line; i <= to.line; ++i) { + if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;} + } + + var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line]; + // First adjust the line structure, taking some care to leave highlighting intact. + if (firstLine == lastLine) { + if (newText.length == 1) + firstLine.replace(from.ch, to.ch, newText[0]); + else { + lastLine = firstLine.split(to.ch, newText[newText.length-1]); + var spliceargs = [from.line + 1, nlines]; + firstLine.replace(from.ch, firstLine.text.length, newText[0]); + for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i])); + spliceargs.push(lastLine); + lines.splice.apply(lines, spliceargs); + } + } + else if (newText.length == 1) { + firstLine.replace(from.ch, firstLine.text.length, newText[0] + lastLine.text.slice(to.ch)); + lines.splice(from.line + 1, nlines); + } + else { + var spliceargs = [from.line + 1, nlines - 1]; + firstLine.replace(from.ch, firstLine.text.length, newText[0]); + lastLine.replace(0, to.ch, newText[newText.length-1]); + for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i])); + lines.splice.apply(lines, spliceargs); + } + + + for (var i = from.line, e = i + newText.length; i < e; ++i) { + var l = lines[i].text; + if (l.length > maxLineLength) { + maxLine = l; maxLineLength = l.length; + recomputeMaxLength = false; + } + } + if (recomputeMaxLength) { + maxLineLength = 0; maxLine = ""; + for (var i = 0, e = lines.length; i < e; ++i) { + var l = lines[i].text; + if (l.length > maxLineLength) { + maxLineLength = l.length; maxLine = l; + } + } + } + + // Add these lines to the work array, so that they will be + // highlighted. Adjust work lines if lines were added/removed. + var newWork = [], lendiff = newText.length - nlines - 1; + for (var i = 0, l = work.length; i < l; ++i) { + var task = work[i]; + if (task < from.line) newWork.push(task); + else if (task > to.line) newWork.push(task + lendiff); + } + if (newText.length < 5) { + highlightLines(from.line, from.line + newText.length); + newWork.push(from.line + newText.length); + } else { + newWork.push(from.line); + } + work = newWork; + startWorker(100); + // Remember that these lines changed, for updating the display + changes.push({from: from.line, to: to.line + 1, diff: lendiff}); + textChanged = {from: from, to: to, text: newText}; + + // Update the selection + function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} + setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line)); + + // Make sure the scroll-size div has the correct height. + code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px"; + } + + function replaceRange(code, from, to) { + from = clipPos(from); + if (!to) to = from; else to = clipPos(to); + code = splitLines(code); + function adjustPos(pos) { + if (posLess(pos, from)) return pos; + if (!posLess(to, pos)) return end; + var line = pos.line + code.length - (to.line - from.line) - 1; + var ch = pos.ch; + if (pos.line == to.line) + ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); + return {line: line, ch: ch}; + } + var end; + replaceRange1(code, from, to, function(end1) { + end = end1; + return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; + }); + return end; + } + function replaceSelection(code, collapse) { + replaceRange1(splitLines(code), sel.from, sel.to, function(end) { + if (collapse == "end") return {from: end, to: end}; + else if (collapse == "start") return {from: sel.from, to: sel.from}; + else return {from: sel.from, to: end}; + }); + } + function replaceRange1(code, from, to, computeSel) { + var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; + var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); + updateLines(from, to, code, newSel.from, newSel.to); + } + + function getRange(from, to) { + var l1 = from.line, l2 = to.line; + if (l1 == l2) return lines[l1].text.slice(from.ch, to.ch); + var code = [lines[l1].text.slice(from.ch)]; + for (var i = l1 + 1; i < l2; ++i) code.push(lines[i].text); + code.push(lines[l2].text.slice(0, to.ch)); + return code.join("\n"); + } + function getSelection() { + return getRange(sel.from, sel.to); + } + + var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll + function slowPoll() { + if (pollingFast) return; + poll.set(2000, function() { + startOperation(); + readInput(); + if (focused) slowPoll(); + endOperation(); + }); + } + function fastPoll(keyId) { + var missed = false; + pollingFast = true; + function p() { + startOperation(); + var changed = readInput(); + if (changed == "moved" && keyId) movementKeys[keyId] = true; + if (!changed && !missed) {missed = true; poll.set(80, p);} + else {pollingFast = false; slowPoll();} + endOperation(); + } + poll.set(20, p); + } + + // Inspects the textarea, compares its state (content, selection) + // to the data in the editing variable, and updates the editor + // content or cursor if something changed. + function readInput() { + if (leaveInputAlone) return; + var changed = false, text = input.value, sr = selRange(input); + if (!sr) return false; + var changed = editing.text != text, rs = reducedSelection; + var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end); + if (!moved && !rs) return false; + if (changed) { + shiftSelecting = reducedSelection = null; + if (options.readOnly) {updateInput = true; return "changed";} + } + + // Compute selection start and end based on start/end offsets in textarea + function computeOffset(n, startLine) { + var pos = 0; + for (;;) { + var found = text.indexOf("\n", pos); + if (found == -1 || (text.charAt(found-1) == "\r" ? found - 1 : found) >= n) + return {line: startLine, ch: n - pos}; + ++startLine; + pos = found + 1; + } + } + var from = computeOffset(sr.start, editing.from), + to = computeOffset(sr.end, editing.from); + // Here we have to take the reducedSelection hack into account, + // so that you can, for example, press shift-up at the start of + // your selection and have the right thing happen. + if (rs) { + var head = sr.start == rs.anchor ? to : from; + var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to; + if (sel.inverted = posLess(head, tail)) { from = head; to = tail; } + else { reducedSelection = null; from = tail; to = head; } + } + + // In some cases (cursor on same line as before), we don't have + // to update the textarea content at all. + if (from.line == to.line && from.line == sel.from.line && from.line == sel.to.line && !shiftSelecting) + updateInput = false; + + // Magic mess to extract precise edited range from the changed + // string. + if (changed) { + var start = 0, end = text.length, len = Math.min(end, editing.text.length); + var c, line = editing.from, nl = -1; + while (start < len && (c = text.charAt(start)) == editing.text.charAt(start)) { + ++start; + if (c == "\n") {line++; nl = start;} + } + var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length; + for (;;) { + c = editing.text.charAt(edend); + if (text.charAt(end) != c) {++end; ++edend; break;} + if (c == "\n") endline--; + if (edend <= start || end <= start) break; + --end; --edend; + } + var nl = editing.text.lastIndexOf("\n", edend - 1), endch = nl == -1 ? edend : edend - nl - 1; + updateLines({line: line, ch: ch}, {line: endline, ch: endch}, splitLines(text.slice(start, end)), from, to); + if (line != endline || from.line != line) updateInput = true; + } + else setSelection(from, to); + + editing.text = text; editing.start = sr.start; editing.end = sr.end; + return changed ? "changed" : moved ? "moved" : false; + } + + // Set the textarea content and selection range to match the + // editor state. + function prepareInput() { + var text = []; + var from = Math.max(0, sel.from.line - 1), to = Math.min(lines.length, sel.to.line + 2); + for (var i = from; i < to; ++i) text.push(lines[i].text); + text = input.value = text.join(lineSep); + var startch = sel.from.ch, endch = sel.to.ch; + for (var i = from; i < sel.from.line; ++i) + startch += lineSep.length + lines[i].text.length; + for (var i = from; i < sel.to.line; ++i) + endch += lineSep.length + lines[i].text.length; + editing = {text: text, from: from, to: to, start: startch, end: endch}; + setSelRange(input, startch, reducedSelection ? startch : endch); + } + function focusInput() { + if (options.readOnly != "nocursor") input.focus(); + } + + function scrollCursorIntoView() { + var cursor = localCoords(sel.inverted ? sel.from : sel.to); + return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot); + } + function scrollIntoView(x1, y1, x2, y2) { + var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight(); + y1 += pt; y2 += pt; x1 += pl; x2 += pl; + var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true; + if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;} + else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;} + + var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; + if (x1 < screenleft) { + if (x1 < 50) x1 = 0; + scroller.scrollLeft = Math.max(0, x1 - 10); + scrolled = true; + } + else if (x2 > screenw + screenleft) { + scroller.scrollLeft = x2 + 10 - screenw; + scrolled = true; + if (x2 > code.clientWidth) result = false; + } + if (scrolled && options.onScroll) options.onScroll(instance); + return result; + } + + function visibleLines() { + var lh = lineHeight(), top = scroller.scrollTop - paddingTop(); + return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))), + to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))}; + } + // Uses a set of changes plus the current scroll position to + // determine which DOM updates have to be made, and makes the + // updates. + function updateDisplay(changes) { + if (!scroller.clientWidth) { + showingFrom = showingTo = 0; + return; + } + // First create a range of theoretically intact lines, and punch + // holes in that using the change info. + var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}]; + for (var i = 0, l = changes.length || 0; i < l; ++i) { + var change = changes[i], intact2 = [], diff = change.diff || 0; + for (var j = 0, l2 = intact.length; j < l2; ++j) { + var range = intact[j]; + if (change.to <= range.from) + intact2.push({from: range.from + diff, to: range.to + diff, domStart: range.domStart}); + else if (range.to <= change.from) + intact2.push(range); + else { + if (change.from > range.from) + intact2.push({from: range.from, to: change.from, domStart: range.domStart}) + if (change.to < range.to) + intact2.push({from: change.to + diff, to: range.to + diff, + domStart: range.domStart + (change.to - range.from)}); + } + } + intact = intact2; + } + + // Then, determine which lines we'd want to see, and which + // updates have to be made to get there. + var visible = visibleLines(); + var from = Math.min(showingFrom, Math.max(visible.from - 3, 0)), + to = Math.min(lines.length, Math.max(showingTo, visible.to + 3)), + updates = [], domPos = 0, domEnd = showingTo - showingFrom, pos = from, changedLines = 0; + + for (var i = 0, l = intact.length; i < l; ++i) { + var range = intact[i]; + if (range.to <= from) continue; + if (range.from >= to) break; + if (range.domStart > domPos || range.from > pos) { + updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos}); + changedLines += range.from - pos; + } + pos = range.to; + domPos = range.domStart + (range.to - range.from); + } + if (domPos != domEnd || pos != to) { + changedLines += Math.abs(to - pos); + updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos}); + } + + if (!updates.length) return; + lineDiv.style.display = "none"; + // If more than 30% of the screen needs update, just do a full + // redraw (which is quicker than patching) + if (changedLines > (visible.to - visible.from) * .3) + refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length)); + // Otherwise, only update the stuff that needs updating. + else + patchDisplay(updates); + lineDiv.style.display = ""; + + // Position the mover div to align with the lines it's supposed + // to be showing (which will cover the visible display) + var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight; + showingFrom = from; showingTo = to; + mover.style.top = (from * lineHeight()) + "px"; + if (different) { + lastHeight = scroller.clientHeight; + code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px"; + updateGutter(); + } + + var textWidth = stringWidth(maxLine); + if (textWidth > scroller.clientWidth) { + lineSpace.style.width = textWidth + "px"; + // Needed to prevent odd wrapping/hiding of widgets placed in here. + code.style.width = scroller.scrollWidth + "px"; + } else { + lineSpace.style.width = code.style.width = ""; + } + + // Since this is all rather error prone, it is honoured with the + // only assertion in the whole file. + if (lineDiv.childNodes.length != showingTo - showingFrom) + throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) + + " nodes=" + lineDiv.childNodes.length); + updateCursor(); + } + + function refreshDisplay(from, to) { + var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start); + for (var i = from; i < to; ++i) { + var ch1 = null, ch2 = null; + if (inSel) { + ch1 = 0; + if (sel.to.line == i) {inSel = false; ch2 = sel.to.ch;} + } + else if (sel.from.line == i) { + if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;} + else {inSel = true; ch1 = sel.from.ch;} + } + html.push(lines[i].getHTML(ch1, ch2, true)); + } + lineDiv.innerHTML = html.join(""); + } + function patchDisplay(updates) { + // Slightly different algorithm for IE (badInnerHTML), since + // there .innerHTML on PRE nodes is dumb, and discards + // whitespace. + var sfrom = sel.from.line, sto = sel.to.line, off = 0, + scratch = badInnerHTML && targetDocument.createElement("div"); + for (var i = 0, e = updates.length; i < e; ++i) { + var rec = updates[i]; + var extra = (rec.to - rec.from) - rec.domSize; + var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null; + if (badInnerHTML) + for (var j = Math.max(-extra, rec.domSize); j > 0; --j) + lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild); + else if (extra) { + for (var j = Math.max(0, extra); j > 0; --j) + lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter); + for (var j = Math.max(0, -extra); j > 0; --j) + lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild); + } + var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from; + for (var j = rec.from; j < rec.to; ++j) { + var ch1 = null, ch2 = null; + if (inSel) { + ch1 = 0; + if (sto == j) {inSel = false; ch2 = sel.to.ch;} + } + else if (sfrom == j) { + if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;} + else {inSel = true; ch1 = sel.from.ch;} + } + if (badInnerHTML) { + scratch.innerHTML = lines[j].getHTML(ch1, ch2, true); + lineDiv.insertBefore(scratch.firstChild, nodeAfter); + } + else { + node.innerHTML = lines[j].getHTML(ch1, ch2, false); + node.className = lines[j].className || ""; + node = node.nextSibling; + } + } + off += extra; + } + } + + function updateGutter() { + if (!options.gutter && !options.lineNumbers) return; + var hText = mover.offsetHeight, hEditor = scroller.clientHeight; + gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; + var html = []; + for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) { + var marker = lines[i].gutterMarker; + var text = options.lineNumbers ? i + options.firstLineNumber : null; + if (marker && marker.text) + text = marker.text.replace("%N%", text != null ? text : ""); + else if (text == null) + text = "\u00a0"; + html.push((marker && marker.style ? '
    ' : "
    "), text, "
    "); + } + gutter.style.display = "none"; + gutterText.innerHTML = html.join(""); + var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = ""; + while (val.length + pad.length < minwidth) pad += "\u00a0"; + if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild); + gutter.style.display = ""; + lineSpace.style.marginLeft = gutter.offsetWidth + "px"; + } + function updateCursor() { + var head = sel.inverted ? sel.from : sel.to, lh = lineHeight(); + var x = charX(head.line, head.ch) + "px", y = (head.line - showingFrom) * lh + "px"; + inputDiv.style.top = (head.line * lh - scroller.scrollTop) + "px"; + if (posEq(sel.from, sel.to)) { + cursor.style.top = y; cursor.style.left = x; + cursor.style.display = ""; + } + else cursor.style.display = "none"; + } + + function setSelectionUser(from, to) { + var sh = shiftSelecting && clipPos(shiftSelecting); + if (sh) { + if (posLess(sh, from)) from = sh; + else if (posLess(to, sh)) to = sh; + } + setSelection(from, to); + } + // Update the selection. Last two args are only used by + // updateLines, since they have to be expressed in the line + // numbers before the update. + function setSelection(from, to, oldFrom, oldTo) { + if (posEq(sel.from, from) && posEq(sel.to, to)) return; + if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} + + if (posEq(from, to)) sel.inverted = false; + else if (posEq(from, sel.to)) sel.inverted = false; + else if (posEq(to, sel.from)) sel.inverted = true; + + // Some ugly logic used to only mark the lines that actually did + // see a change in selection as changed, rather than the whole + // selected range. + if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} + if (posEq(from, to)) { + if (!posEq(sel.from, sel.to)) + changes.push({from: oldFrom, to: oldTo + 1}); + } + else if (posEq(sel.from, sel.to)) { + changes.push({from: from.line, to: to.line + 1}); + } + else { + if (!posEq(from, sel.from)) { + if (from.line < oldFrom) + changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1}); + else + changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1}); + } + if (!posEq(to, sel.to)) { + if (to.line < oldTo) + changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1}); + else + changes.push({from: Math.max(from.line, oldTo), to: to.line + 1}); + } + } + sel.from = from; sel.to = to; + selectionChanged = true; + } + function setCursor(line, ch, user) { + var pos = clipPos({line: line, ch: ch || 0}); + (user ? setSelectionUser : setSelection)(pos, pos); + } + + function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));} + function clipPos(pos) { + if (pos.line < 0) return {line: 0, ch: 0}; + if (pos.line >= lines.length) return {line: lines.length-1, ch: lines[lines.length-1].text.length}; + var ch = pos.ch, linelen = lines[pos.line].text.length; + if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; + else if (ch < 0) return {line: pos.line, ch: 0}; + else return pos; + } + + function scrollPage(down) { + var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to; + setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true); + } + function scrollEnd(top) { + var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length}; + setSelectionUser(pos, pos); + } + function selectAll() { + var endLine = lines.length - 1; + setSelection({line: 0, ch: 0}, {line: endLine, ch: lines[endLine].text.length}); + } + function selectWordAt(pos) { + var line = lines[pos.line].text; + var start = pos.ch, end = pos.ch; + while (start > 0 && /\w/.test(line.charAt(start - 1))) --start; + while (end < line.length && /\w/.test(line.charAt(end))) ++end; + setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end}); + } + function selectLine(line) { + setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length}); + } + function handleEnter() { + replaceSelection("\n", "end"); + if (options.enterMode != "flat") + indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart"); + } + function handleTab(shift) { + function indentSelected(mode) { + if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); + var e = sel.to.line - (sel.to.ch ? 1 : 0); + for (var i = sel.from.line; i < e; ++i) indentLine(i, mode); + } + shiftSelecting = null; + switch (options.tabMode) { + case "default": + return false; + case "indent": + indentSelected("smart"); + break; + case "classic": + if (posEq(sel.from, sel.to)) { + if (shift) indentLine(sel.from.line, "smart"); + else replaceSelection("\t", "end"); + break; + } + case "shift": + indentSelected(shift ? "subtract" : "add"); + break; + } + return true; + } + + function indentLine(n, how) { + if (how == "smart") { + if (!mode.indent) how = "prev"; + else var state = getStateBefore(n); + } + + var line = lines[n], curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (how == "prev") { + if (n) indentation = lines[n-1].indentation(); + else indentation = 0; + } + else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length)); + else if (how == "add") indentation = curSpace + options.indentUnit; + else if (how == "subtract") indentation = curSpace - options.indentUnit; + indentation = Math.max(0, indentation); + var diff = indentation - curSpace; + + if (!diff) { + if (sel.from.line != n && sel.to.line != n) return; + var indentString = curSpaceString; + } + else { + var indentString = "", pos = 0; + if (options.indentWithTabs) + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} + while (pos < indentation) {++pos; indentString += " ";} + } + + replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + } + + function loadMode() { + mode = CodeMirror.getMode(options, options.mode); + for (var i = 0, l = lines.length; i < l; ++i) + lines[i].stateAfter = null; + work = [0]; + startWorker(); + } + function gutterChanged() { + var visible = options.gutter || options.lineNumbers; + gutter.style.display = visible ? "" : "none"; + if (visible) updateGutter(); + else lineDiv.parentNode.style.marginLeft = 0; + } + + function markText(from, to, className) { + from = clipPos(from); to = clipPos(to); + var accum = []; + function add(line, from, to, className) { + var line = lines[line], mark = line.addMark(from, to, className); + mark.line = line; + accum.push(mark); + } + if (from.line == to.line) add(from.line, from.ch, to.ch, className); + else { + add(from.line, from.ch, null, className); + for (var i = from.line + 1, e = to.line; i < e; ++i) + add(i, 0, null, className); + add(to.line, 0, to.ch, className); + } + changes.push({from: from.line, to: to.line + 1}); + return function() { + var start, end; + for (var i = 0; i < accum.length; ++i) { + var mark = accum[i], found = indexOf(lines, mark.line); + mark.line.removeMark(mark); + if (found > -1) { + if (start == null) start = found; + end = found; + } + } + if (start != null) changes.push({from: start, to: end + 1}); + }; + } + + function addGutterMarker(line, text, className) { + if (typeof line == "number") line = lines[clipLine(line)]; + line.gutterMarker = {text: text, style: className}; + updateGutter(); + return line; + } + function removeGutterMarker(line) { + if (typeof line == "number") line = lines[clipLine(line)]; + line.gutterMarker = null; + updateGutter(); + } + function setLineClass(line, className) { + if (typeof line == "number") { + var no = line; + line = lines[clipLine(line)]; + } + else { + var no = indexOf(lines, line); + if (no == -1) return null; + } + if (line.className != className) { + line.className = className; + changes.push({from: no, to: no + 1}); + } + return line; + } + + function lineInfo(line) { + if (typeof line == "number") { + var n = line; + line = lines[line]; + if (!line) return null; + } + else { + var n = indexOf(lines, line); + if (n == -1) return null; + } + var marker = line.gutterMarker; + return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style}; + } + + function stringWidth(str) { + measure.innerHTML = "
    x
    "; + measure.firstChild.firstChild.firstChild.nodeValue = str; + return measure.firstChild.firstChild.offsetWidth || 10; + } + // These are used to go from pixel positions to character + // positions, taking varying character widths into account. + function charX(line, pos) { + if (pos == 0) return 0; + measure.innerHTML = "
    " + lines[line].getHTML(null, null, false, pos) + "
    "; + return measure.firstChild.firstChild.offsetWidth; + } + function charFromX(line, x) { + if (x <= 0) return 0; + var lineObj = lines[line], text = lineObj.text; + function getX(len) { + measure.innerHTML = "
    " + lineObj.getHTML(null, null, false, len) + "
    "; + return measure.firstChild.firstChild.offsetWidth; + } + var from = 0, fromX = 0, to = text.length, toX; + // Guess a suitable upper bound for our search. + var estimated = Math.min(to, Math.ceil(x / stringWidth("x"))); + for (;;) { + var estX = getX(estimated); + if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); + else {toX = estX; to = estimated; break;} + } + if (x > toX) return to; + // Try to guess a suitable lower bound as well. + estimated = Math.floor(to * 0.8); estX = getX(estimated); + if (estX < x) {from = estimated; fromX = estX;} + // Do a binary search between these bounds. + for (;;) { + if (to - from <= 1) return (toX - x > x - fromX) ? from : to; + var middle = Math.ceil((from + to) / 2), middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX;} + else {from = middle; fromX = middleX;} + } + } + + function localCoords(pos, inLineWrap) { + var lh = lineHeight(), line = pos.line - (inLineWrap ? showingFrom : 0); + return {x: charX(pos.line, pos.ch), y: line * lh, yBot: (line + 1) * lh}; + } + function pageCoords(pos) { + var local = localCoords(pos, true), off = eltOffset(lineSpace); + return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; + } + + function lineHeight() { + var nlines = lineDiv.childNodes.length; + if (nlines) return (lineDiv.offsetHeight / nlines) || 1; + measure.innerHTML = "
    x
    "; + return measure.firstChild.offsetHeight || 1; + } + function paddingTop() {return lineSpace.offsetTop;} + function paddingLeft() {return lineSpace.offsetLeft;} + + function posFromMouse(e, liberal) { + var offW = eltOffset(scroller, true), x, y; + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.e.clientX; y = e.e.clientY; } catch (e) { return null; } + // This is a mess of a heuristic to try and determine whether a + // scroll-bar was clicked or not, and to return null if one was + // (and !liberal). + if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) + return null; + var offL = eltOffset(lineSpace, true); + var line = showingFrom + Math.floor((y - offL.top) / lineHeight()); + return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)}); + } + function onContextMenu(e) { + var pos = posFromMouse(e); + if (!pos || window.opera) return; // Opera is difficult. + if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) + operation(setCursor)(pos.line, pos.ch); + + var oldCSS = input.style.cssText; + inputDiv.style.position = "absolute"; + input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.pageY() - 1) + + "px; left: " + (e.pageX() - 1) + "px; z-index: 1000; background: white; " + + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + leaveInputAlone = true; + var val = input.value = getSelection(); + focusInput(); + setSelRange(input, 0, input.value.length); + function rehide() { + var newVal = splitLines(input.value).join("\n"); + if (newVal != val) operation(replaceSelection)(newVal, "end"); + inputDiv.style.position = "relative"; + input.style.cssText = oldCSS; + leaveInputAlone = false; + prepareInput(); + slowPoll(); + } + + if (gecko) { + e.stop(); + var mouseup = connect(window, "mouseup", function() { + mouseup(); + setTimeout(rehide, 20); + }, true); + } + else { + setTimeout(rehide, 50); + } + } + + // Cursor-blinking + function restartBlink() { + clearInterval(blinker); + var on = true; + cursor.style.visibility = ""; + blinker = setInterval(function() { + cursor.style.visibility = (on = !on) ? "" : "hidden"; + }, 650); + } + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; + function matchBrackets(autoclear) { + var head = sel.inverted ? sel.from : sel.to, line = lines[head.line], pos = head.ch - 1; + var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; + if (!match) return; + var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; + for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) + if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} + + var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; + function scan(line, from, to) { + if (!line.text) return; + var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; + for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { + var text = st[i]; + if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;} + for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { + if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { + var match = matching[cur]; + if (match.charAt(1) == ">" == forward) stack.push(cur); + else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; + else if (!stack.length) return {pos: pos, match: true}; + } + } + } + } + for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) { + var line = lines[i], first = i == head.line; + var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); + if (found) break; + } + if (!found) found = {pos: null, match: false}; + var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), + two = found.pos != null + ? markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style) + : function() {}; + var clear = operation(function(){one(); two();}); + if (autoclear) setTimeout(clear, 800); + else bracketHighlighted = clear; + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(n) { + var minindent, minline; + for (var search = n, lim = n - 40; search > lim; --search) { + if (search == 0) return 0; + var line = lines[search-1]; + if (line.stateAfter) return search; + var indented = line.indentation(); + if (minline == null || minindent > indented) { + minline = search; + minindent = indented; + } + } + return minline; + } + function getStateBefore(n) { + var start = findStartLine(n), state = start && lines[start-1].stateAfter; + if (!state) state = startState(mode); + else state = copyState(mode, state); + for (var i = start; i < n; ++i) { + var line = lines[i]; + line.highlight(mode, state); + line.stateAfter = copyState(mode, state); + } + if (!lines[n].stateAfter) work.push(n); + return state; + } + function highlightLines(start, end) { + var state = getStateBefore(start); + for (var i = start; i < end; ++i) { + var line = lines[i]; + line.highlight(mode, state); + line.stateAfter = copyState(mode, state); + } + } + function highlightWorker() { + var end = +new Date + options.workTime; + var foundWork = work.length; + while (work.length) { + if (!lines[showingFrom].stateAfter) var task = showingFrom; + else var task = work.pop(); + if (task >= lines.length) continue; + var start = findStartLine(task), state = start && lines[start-1].stateAfter; + if (state) state = copyState(mode, state); + else state = startState(mode); + + var unchanged = 0, compare = mode.compareStates; + for (var i = start, l = lines.length; i < l; ++i) { + var line = lines[i], hadState = line.stateAfter; + if (+new Date > end) { + work.push(i); + startWorker(options.workDelay); + changes.push({from: task, to: i}); + return; + } + var changed = line.highlight(mode, state); + line.stateAfter = copyState(mode, state); + if (compare) { + if (hadState && compare(hadState, state)) break; + } else { + if (changed || !hadState) unchanged = 0; + else if (++unchanged > 3) break; + } + } + changes.push({from: task, to: i}); + } + if (foundWork && options.onHighlightComplete) + options.onHighlightComplete(instance); + } + function startWorker(time) { + if (!work.length) return; + highlight.set(time, operation(highlightWorker)); + } + + // Operations are used to wrap changes in such a way that each + // change won't have to update the cursor and display (which would + // be awkward, slow, and error-prone), but instead updates are + // batched and then all combined and executed at once. + function startOperation() { + updateInput = null; changes = []; textChanged = selectionChanged = false; + } + function endOperation() { + var reScroll = false; + if (selectionChanged) reScroll = !scrollCursorIntoView(); + if (changes.length) updateDisplay(changes); + else if (selectionChanged) updateCursor(); + if (reScroll) scrollCursorIntoView(); + if (selectionChanged) restartBlink(); + + // updateInput can be set to a boolean value to force/prevent an + // update. + if (focused && !leaveInputAlone && + (updateInput === true || (updateInput !== false && selectionChanged))) + prepareInput(); + + if (selectionChanged && options.matchBrackets) + setTimeout(operation(function() { + if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} + matchBrackets(false); + }), 20); + var tc = textChanged; // textChanged can be reset by cursoractivity callback + if (selectionChanged && options.onCursorActivity) + options.onCursorActivity(instance); + if (tc && options.onChange && instance) + options.onChange(instance, tc); + } + var nestedOperation = 0; + function operation(f) { + return function() { + if (!nestedOperation++) startOperation(); + try {var result = f.apply(this, arguments);} + finally {if (!--nestedOperation) endOperation();} + return result; + }; + } + + function SearchCursor(query, pos, caseFold) { + this.atOccurrence = false; + if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase(); + + if (pos && typeof pos == "object") pos = clipPos(pos); + else pos = {line: 0, ch: 0}; + this.pos = {from: pos, to: pos}; + + // The matches method is filled in based on the type of query. + // It takes a position and a direction, and returns an object + // describing the next occurrence of the query, or null if no + // more matches were found. + if (typeof query != "string") // Regexp match + this.matches = function(reverse, pos) { + if (reverse) { + var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0; + while (match) { + var ind = line.indexOf(match[0]); + start += ind; + line = line.slice(ind + 1); + var newmatch = line.match(query); + if (newmatch) match = newmatch; + else break; + start++; + } + } + else { + var line = lines[pos.line].text.slice(pos.ch), match = line.match(query), + start = match && pos.ch + line.indexOf(match[0]); + } + if (match) + return {from: {line: pos.line, ch: start}, + to: {line: pos.line, ch: start + match[0].length}, + match: match}; + }; + else { // String query + if (caseFold) query = query.toLowerCase(); + var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; + var target = query.split("\n"); + // Different methods for single-line and multi-line queries + if (target.length == 1) + this.matches = function(reverse, pos) { + var line = fold(lines[pos.line].text), len = query.length, match; + if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) + : (match = line.indexOf(query, pos.ch)) != -1) + return {from: {line: pos.line, ch: match}, + to: {line: pos.line, ch: match + len}}; + }; + else + this.matches = function(reverse, pos) { + var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text); + var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); + if (reverse ? offsetA >= pos.ch || offsetA != match.length + : offsetA <= pos.ch || offsetA != line.length - match.length) + return; + for (;;) { + if (reverse ? !ln : ln == lines.length - 1) return; + line = fold(lines[ln += reverse ? -1 : 1].text); + match = target[reverse ? --idx : ++idx]; + if (idx > 0 && idx < target.length - 1) { + if (line != match) return; + else continue; + } + var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); + if (reverse ? offsetB != line.length - match.length : offsetB != match.length) + return; + var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB}; + return {from: reverse ? end : start, to: reverse ? start : end}; + } + }; + } + } + + SearchCursor.prototype = { + findNext: function() {return this.find(false);}, + findPrevious: function() {return this.find(true);}, + + find: function(reverse) { + var self = this, pos = clipPos(reverse ? this.pos.from : this.pos.to); + function savePosAndFail(line) { + var pos = {line: line, ch: 0}; + self.pos = {from: pos, to: pos}; + self.atOccurrence = false; + return false; + } + + for (;;) { + if (this.pos = this.matches(reverse, pos)) { + this.atOccurrence = true; + return this.pos.match || true; + } + if (reverse) { + if (!pos.line) return savePosAndFail(0); + pos = {line: pos.line-1, ch: lines[pos.line-1].text.length}; + } + else { + if (pos.line == lines.length - 1) return savePosAndFail(lines.length); + pos = {line: pos.line+1, ch: 0}; + } + } + }, + + from: function() {if (this.atOccurrence) return copyPos(this.pos.from);}, + to: function() {if (this.atOccurrence) return copyPos(this.pos.to);}, + + replace: function(newText) { + var self = this; + if (this.atOccurrence) + operation(function() { + self.pos.to = replaceRange(newText, self.pos.from, self.pos.to); + })(); + } + }; + + for (var ext in extensions) + if (extensions.propertyIsEnumerable(ext) && + !instance.propertyIsEnumerable(ext)) + instance[ext] = extensions[ext]; + return instance; + } // (end of function CodeMirror) + + // The default configuration options. + CodeMirror.defaults = { + value: "", + mode: null, + theme: "default", + indentUnit: 2, + indentWithTabs: false, + tabMode: "classic", + enterMode: "indent", + electricChars: true, + onKeyEvent: null, + lineNumbers: false, + gutter: false, + firstLineNumber: 1, + readOnly: false, + onChange: null, + onCursorActivity: null, + onGutterClick: null, + onHighlightComplete: null, + onFocus: null, onBlur: null, onScroll: null, + matchBrackets: false, + workTime: 100, + workDelay: 200, + undoDepth: 40, + tabindex: null, + document: window.document + }; + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + CodeMirror.defineMode = function(name, mode) { + if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; + modes[name] = mode; + }; + CodeMirror.defineMIME = function(mime, spec) { + mimeModes[mime] = spec; + }; + CodeMirror.getMode = function(options, spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + spec = mimeModes[spec]; + if (typeof spec == "string") + var mname = spec, config = {}; + else if (spec != null) + var mname = spec.name, config = spec; + var mfactory = modes[mname]; + if (!mfactory) { + if (window.console) console.warn("No mode " + mname + " found, falling back to plain text."); + return CodeMirror.getMode(options, "text/plain"); + } + return mfactory(options, config || {}); + }; + CodeMirror.listModes = function() { + var list = []; + for (var m in modes) + if (modes.propertyIsEnumerable(m)) list.push(m); + return list; + }; + CodeMirror.listMIMEs = function() { + var list = []; + for (var m in mimeModes) + if (mimeModes.propertyIsEnumerable(m)) list.push(m); + return list; + }; + + var extensions = {}; + CodeMirror.defineExtension = function(name, func) { + extensions[name] = func; + }; + + CodeMirror.fromTextArea = function(textarea, options) { + if (!options) options = {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabindex) + options.tabindex = textarea.tabindex; + + function save() {textarea.value = instance.getValue();} + if (textarea.form) { + // Deplorable hack to make the submit method do the right thing. + var rmSubmit = connect(textarea.form, "submit", save, true); + if (typeof textarea.form.submit == "function") { + var realSubmit = textarea.form.submit; + function wrappedSubmit() { + save(); + textarea.form.submit = realSubmit; + textarea.form.submit(); + textarea.form.submit = wrappedSubmit; + } + textarea.form.submit = wrappedSubmit; + } + } + + textarea.style.display = "none"; + var instance = CodeMirror(function(node) { + textarea.parentNode.insertBefore(node, textarea.nextSibling); + }, options); + instance.save = save; + instance.toTextArea = function() { + save(); + textarea.parentNode.removeChild(instance.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + rmSubmit(); + if (typeof textarea.form.submit == "function") + textarea.form.submit = realSubmit; + } + }; + return instance; + }; + + // Utility functions for working with state. Exported because modes + // sometimes need to do this. + function copyState(mode, state) { + if (state === true) return state; + if (mode.copyState) return mode.copyState(state); + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) val = val.concat([]); + nstate[n] = val; + } + return nstate; + } + CodeMirror.startState = startState; + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; + } + CodeMirror.copyState = copyState; + + // The character stream used by a mode's parser. + function StringStream(string) { + this.pos = this.start = 0; + this.string = string; + } + StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == 0;}, + peek: function() {return this.string.charAt(this.pos);}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.start; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() {return countColumn(this.string, this.start);}, + indentation: function() {return countColumn(this.string);}, + match: function(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } + else { + var match = this.string.slice(this.pos).match(pattern); + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);} + }; + CodeMirror.StringStream = StringStream; + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + function Line(text, styles) { + this.styles = styles || [text, null]; + this.stateAfter = null; + this.text = text; + this.marked = this.gutterMarker = this.className = null; + } + Line.prototype = { + // Replace a piece of a line, keeping the styles around it intact. + replace: function(from, to, text) { + var st = [], mk = this.marked; + copyStyles(0, from, this.styles, st); + if (text) st.push(text, null); + copyStyles(to, this.text.length, this.styles, st); + this.styles = st; + this.text = this.text.slice(0, from) + text + this.text.slice(to); + this.stateAfter = null; + if (mk) { + var diff = text.length - (to - from), end = this.text.length; + function fix(n) {return n <= Math.min(to, to + diff) ? n : n + diff;} + for (var i = 0; i < mk.length; ++i) { + var mark = mk[i], del = false; + if (mark.from >= end) del = true; + else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);} + if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;} + } + } + }, + // Split a line in two, again keeping styles intact. + split: function(pos, textBefore) { + var st = [textBefore, null]; + copyStyles(pos, this.text.length, this.styles, st); + return new Line(textBefore + this.text.slice(pos), st); + }, + addMark: function(from, to, style) { + var mk = this.marked, mark = {from: from, to: to, style: style}; + if (this.marked == null) this.marked = []; + this.marked.push(mark); + this.marked.sort(function(a, b){return a.from - b.from;}); + return mark; + }, + removeMark: function(mark) { + var mk = this.marked; + if (!mk) return; + for (var i = 0; i < mk.length; ++i) + if (mk[i] == mark) {mk.splice(i, 1); break;} + }, + // Run the given mode's parser over a line, update the styles + // array, which contains alternating fragments of text and CSS + // classes. + highlight: function(mode, state) { + var stream = new StringStream(this.text), st = this.styles, pos = 0; + var changed = false, curWord = st[0], prevWord; + if (this.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol()) { + var style = mode.token(stream, state); + var substr = this.text.slice(stream.start, stream.pos); + stream.start = stream.pos; + if (pos && st[pos-1] == style) + st[pos-2] += substr; + else if (substr) { + if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; + st[pos++] = substr; st[pos++] = style; + prevWord = curWord; curWord = st[pos]; + } + // Give up when line is ridiculously long + if (stream.pos > 5000) { + st[pos++] = this.text.slice(stream.pos); st[pos++] = null; + break; + } + } + if (st.length != pos) {st.length = pos; changed = true;} + if (pos && st[pos-2] != prevWord) changed = true; + // Short lines with simple highlights always count as changed, + // because they are likely to highlight the same way in various + // contexts. + return changed || (st.length < 5 && this.text.length < 10); + }, + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(mode, state, ch) { + var txt = this.text, stream = new StringStream(txt); + while (stream.pos < ch && !stream.eol()) { + stream.start = stream.pos; + var style = mode.token(stream, state); + } + return {start: stream.start, + end: stream.pos, + string: stream.current(), + className: style || null, + state: state}; + }, + indentation: function() {return countColumn(this.text);}, + // Produces an HTML fragment for the line, taking selection, + // marking, and highlighting into account. + getHTML: function(sfrom, sto, includePre, endAt) { + var html = []; + if (includePre) + html.push(this.className ? '
    ': "
    ");
    +      function span(text, style) {
    +        if (!text) return;
    +        if (style) html.push('', htmlEscape(text), "");
    +        else html.push(htmlEscape(text));
    +      }
    +      var st = this.styles, allText = this.text, marked = this.marked;
    +      if (sfrom == sto) sfrom = null;
    +      var len = allText.length;
    +      if (endAt != null) len = Math.min(endAt, len);
    +
    +      if (!allText && endAt == null)
    +        span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
    +      else if (!marked && sfrom == null)
    +        for (var i = 0, ch = 0; ch < len; i+=2) {
    +          var str = st[i], l = str.length;
    +          if (ch + l > len) str = str.slice(0, len - ch);
    +          ch += l;
    +          span(str, st[i+1]);
    +        }
    +      else {
    +        var pos = 0, i = 0, text = "", style, sg = 0;
    +        var markpos = -1, mark = null;
    +        function nextMark() {
    +          if (marked) {
    +            markpos += 1;
    +            mark = (markpos < marked.length) ? marked[markpos] : null;
    +          }
    +        }
    +        nextMark();
    +        while (pos < len) {
    +          var upto = len;
    +          var extraStyle = "";
    +          if (sfrom != null) {
    +            if (sfrom > pos) upto = sfrom;
    +            else if (sto == null || sto > pos) {
    +              extraStyle = " CodeMirror-selected";
    +              if (sto != null) upto = Math.min(upto, sto);
    +            }
    +          }
    +          while (mark && mark.to != null && mark.to <= pos) nextMark();
    +          if (mark) {
    +            if (mark.from > pos) upto = Math.min(upto, mark.from);
    +            else {
    +              extraStyle += " " + mark.style;
    +              if (mark.to != null) upto = Math.min(upto, mark.to);
    +            }
    +          }
    +          for (;;) {
    +            var end = pos + text.length;
    +            var apliedStyle = style;
    +            if (extraStyle) apliedStyle = style ? style + extraStyle : extraStyle;
    +            span(end > upto ? text.slice(0, upto - pos) : text, apliedStyle);
    +            if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
    +            pos = end;
    +            text = st[i++]; style = st[i++];
    +          }
    +        }
    +        if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
    +      }
    +      if (includePre) html.push("
    "); + return html.join(""); + } + }; + // Utility used by replace and split above + function copyStyles(from, to, source, dest) { + for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { + var part = source[i], end = pos + part.length; + if (state == 0) { + if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); + if (end >= from) state = 1; + } + else if (state == 1) { + if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); + else dest.push(part, source[i+1]); + } + pos = end; + } + } + + // The history object 'chunks' changes that are made close together + // and at almost the same time into bigger undoable units. + function History() { + this.time = 0; + this.done = []; this.undone = []; + } + History.prototype = { + addChange: function(start, added, old) { + this.undone.length = 0; + var time = +new Date, last = this.done[this.done.length - 1]; + if (time - this.time > 400 || !last || + last.start > start + added || last.start + last.added < start - last.added + last.old.length) + this.done.push({start: start, added: added, old: old}); + else { + var oldoff = 0; + if (start < last.start) { + for (var i = last.start - start - 1; i >= 0; --i) + last.old.unshift(old[i]); + last.added += last.start - start; + last.start = start; + } + else if (last.start < start) { + oldoff = start - last.start; + added += oldoff; + } + for (var i = last.added - oldoff, e = old.length; i < e; ++i) + last.old.push(old[i]); + if (last.added < added) last.added = added; + } + this.time = time; + } + }; + + // Event stopping compatibility wrapper. + function stopEvent() { + if (this.preventDefault) {this.preventDefault(); this.stopPropagation();} + else {this.returnValue = false; this.cancelBubble = true;} + } + // Ensure an event has a stop method. + function addStop(event) { + if (!event.stop) event.stop = stopEvent; + return event; + } + + // Event wrapper, exposing the few operations we need. + function Event(orig) {this.e = orig;} + Event.prototype = { + stop: function() {stopEvent.call(this.e);}, + target: function() {return this.e.target || this.e.srcElement;}, + button: function() { + if (this.e.which) return this.e.which; + else if (this.e.button & 1) return 1; + else if (this.e.button & 2) return 3; + else if (this.e.button & 4) return 2; + }, + pageX: function() { + if (this.e.pageX != null) return this.e.pageX; + var doc = this.target().ownerDocument; + return this.e.clientX + doc.body.scrollLeft + doc.documentElement.scrollLeft; + }, + pageY: function() { + if (this.e.pageY != null) return this.e.pageY; + var doc = this.target().ownerDocument; + return this.e.clientY + doc.body.scrollTop + doc.documentElement.scrollTop; + } + }; + + // Event handler registration. If disconnect is true, it'll return a + // function that unregisters the handler. + function connect(node, type, handler, disconnect) { + function wrapHandler(event) {handler(new Event(event || window.event));} + if (typeof node.addEventListener == "function") { + node.addEventListener(type, wrapHandler, false); + if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);}; + } + else { + node.attachEvent("on" + type, wrapHandler); + if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; + } + } + + function Delayed() {this.id = null;} + Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; + + // Some IE versions don't preserve whitespace when setting the + // innerHTML of a PRE tag. + var badInnerHTML = (function() { + var pre = document.createElement("pre"); + pre.innerHTML = " "; return !pre.innerHTML; + })(); + + var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); + var ie = /MSIE \d/.test(navigator.userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + + var lineSep = "\n"; + // Feature-detect whether newlines in textareas are converted to \r\n + (function () { + var te = document.createElement("textarea"); + te.value = "foo\nbar"; + if (te.value.indexOf("\r") > -1) lineSep = "\r\n"; + }()); + + var tabSize = 8; + var mac = /Mac/.test(navigator.platform); + var movementKeys = {}; + for (var i = 35; i <= 40; ++i) + movementKeys[i] = movementKeys["c" + i] = true; + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) end = string.length; + } + for (var i = 0, n = 0; i < end; ++i) { + if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); + else ++n; + } + return n; + } + + function computedStyle(elt) { + if (elt.currentStyle) return elt.currentStyle; + return window.getComputedStyle(elt, null); + } + // Find the position of an element by following the offsetParent chain. + // If screen==true, it returns screen (rather than page) coordinates. + function eltOffset(node, screen) { + var doc = node.ownerDocument.body; + var x = 0, y = 0, skipDoc = false; + for (var n = node; n; n = n.offsetParent) { + x += n.offsetLeft; y += n.offsetTop; + if (screen && computedStyle(n).position == "fixed") + skipDoc = true; + } + var e = screen && !skipDoc ? null : doc; + for (var n = node.parentNode; n != e; n = n.parentNode) + if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;} + return {left: x, top: y}; + } + // Get a node's text content. + function eltText(node) { + return node.textContent || node.innerText || node.nodeValue || ""; + } + + // Operations on {line, ch} objects. + function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} + function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} + function copyPos(x) {return {line: x.line, ch: x.ch};} + + function htmlEscape(str) { + return str.replace(/[<>&]/g, function(str) { + return str == "&" ? "&" : str == "<" ? "<" : ">"; + }); + } + CodeMirror.htmlEscape = htmlEscape; + + // Used to position the cursor after an undo/redo by finding the + // last edited character. + function editEnd(from, to) { + if (!to) return from ? from.length : 0; + if (!from) return to.length; + for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) + if (from.charAt(i) != to.charAt(j)) break; + return j + 1; + } + + function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLines, selRange, setSelRange; + if ("\n\nb".split(/\n/).length != 3) + splitLines = function(string) { + var pos = 0, nl, result = []; + while ((nl = string.indexOf("\n", pos)) > -1) { + result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl)); + pos = nl + 1; + } + result.push(string.slice(pos)); + return result; + }; + else + splitLines = function(string){return string.split(/\r?\n/);}; + CodeMirror.splitLines = splitLines; + + // Sane model of finding and setting the selection in a textarea + if (window.getSelection) { + selRange = function(te) { + try {return {start: te.selectionStart, end: te.selectionEnd};} + catch(e) {return null;} + }; + if (safari) + // On Safari, selection set with setSelectionRange are in a sort + // of limbo wrt their anchor. If you press shift-left in them, + // the anchor is put at the end, and the selection expanded to + // the left. If you press shift-right, the anchor ends up at the + // front. This is not what CodeMirror wants, so it does a + // spurious modify() call to get out of limbo. + setSelRange = function(te, start, end) { + if (start == end) + te.setSelectionRange(start, end); + else { + te.setSelectionRange(start, end - 1); + window.getSelection().modify("extend", "forward", "character"); + } + }; + else + setSelRange = function(te, start, end) { + try {te.setSelectionRange(start, end);} + catch(e) {} // Fails on Firefox when textarea isn't part of the document + }; + } + // IE model. Don't ask. + else { + selRange = function(te) { + try {var range = te.ownerDocument.selection.createRange();} + catch(e) {return null;} + if (!range || range.parentElement() != te) return null; + var val = te.value, len = val.length, localRange = te.createTextRange(); + localRange.moveToBookmark(range.getBookmark()); + var endRange = te.createTextRange(); + endRange.collapse(false); + + if (localRange.compareEndPoints("StartToEnd", endRange) > -1) + return {start: len, end: len}; + + var start = -localRange.moveStart("character", -len); + for (var i = val.indexOf("\r"); i > -1 && i < start; i = val.indexOf("\r", i+1), start++) {} + + if (localRange.compareEndPoints("EndToEnd", endRange) > -1) + return {start: start, end: len}; + + var end = -localRange.moveEnd("character", -len); + for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {} + return {start: start, end: end}; + }; + setSelRange = function(te, start, end) { + var range = te.createTextRange(); + range.collapse(true); + var endrange = range.duplicate(); + var newlines = 0, txt = te.value; + for (var pos = txt.indexOf("\n"); pos > -1 && pos < start; pos = txt.indexOf("\n", pos + 1)) + ++newlines; + range.move("character", start - newlines); + for (; pos > -1 && pos < end; pos = txt.indexOf("\n", pos + 1)) + ++newlines; + endrange.move("character", end - newlines); + range.setEndPoint("EndToEnd", endrange); + range.select(); + }; + } + + CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; + }); + CodeMirror.defineMIME("text/plain", "null"); + + return CodeMirror; +})(); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/codemirror/javascript.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/codemirror/javascript.js new file mode 100644 index 00000000000..e73a87985f2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/codemirror/javascript.js @@ -0,0 +1,348 @@ +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var jsonMode = parserConfig.json; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + return { + "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, + "var": kw("var"), "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom + }; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|]/; + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + + function nextUntilUnescaped(stream, end) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (next == end && !escaped) + return false; + escaped = !escaped && next == "\\"; + } + return escaped; + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + + function jsTokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") + return chain(stream, state, jsTokenString(ch)); + else if (/[\[\]{}\(\),;\:\.]/.test(ch)) + return ret(ch); + else if (ch == "0" && stream.eat(/x/i)) { + stream.eatWhile(/[\da-f]/i); + return ret("number", "atom"); + } + else if (/\d/.test(ch)) { + stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/); + return ret("number", "atom"); + } + else if (ch == "/") { + if (stream.eat("*")) { + return chain(stream, state, jsTokenComment); + } + else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } + else if (state.reAllowed) { + nextUntilUnescaped(stream, "/"); + stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla + return ret("regexp", "string"); + } + else { + stream.eatWhile(isOperatorChar); + return ret("operator", null, stream.current()); + } + } + else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return ret("operator", null, stream.current()); + } + else { + stream.eatWhile(/[\w\$_]/); + var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; + return known ? ret(known.type, known.style, word) : + ret("variable", "variable", word); + } + } + + function jsTokenString(quote) { + return function(stream, state) { + if (!nextUntilUnescaped(stream, quote)) + state.tokenize = jsTokenBase; + return ret("string", "string"); + }; + } + + function jsTokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = jsTokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function register(varname) { + var state = cx.state; + if (state.context) { + cx.marked = "def"; + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return; + state.localVars = {name: varname, next: state.localVars}; + } + } + + // Combinators + + var defaultVars = {name: "this", next: {name: "arguments"}}; + function pushcontext() { + if (!cx.state.context) cx.state.localVars = defaultVars; + cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; + } + function popcontext() { + cx.state.localVars = cx.state.context.vars; + cx.state.context = cx.state.context.prev; + } + function pushlex(type, info) { + var result = function() { + var state = cx.state; + state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info) + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + return function expecting(type) { + if (type == wanted) return cont(); + else if (wanted == ";") return pass(); + else return cont(arguments.callee); + }; + } + + function statement(type) { + if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "{") return cont(pushlex("}"), block, poplex); + if (type == ";") return cont(); + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), + poplex, statement, poplex); + if (type == "variable") return cont(pushlex("stat"), maybelabel); + if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), + block, poplex, poplex); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), + statement, poplex, popcontext); + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function expression(type) { + if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator); + if (type == "function") return cont(functiondef); + if (type == "keyword c") return cont(expression); + if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator); + if (type == "operator") return cont(expression); + if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator); + if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator); + return cont(); + } + function maybeoperator(type, value) { + if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator); + if (type == "operator") return cont(expression); + if (type == ";") return; + if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator); + if (type == ".") return cont(property, maybeoperator); + if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperator, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type) { + if (type == "variable") cx.marked = "property"; + if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression); + } + function commasep(what, end) { + function proceed(type) { + if (type == ",") return cont(what, proceed); + if (type == end) return cont(); + return cont(expect(end)); + } + return function commaSeparated(type) { + if (type == end) return cont(); + else return pass(what, proceed); + }; + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function vardef1(type, value) { + if (type == "variable"){register(value); return cont(vardef2);} + return cont(); + } + function vardef2(type, value) { + if (value == "=") return cont(expression, vardef2); + if (type == ",") return cont(vardef1); + } + function forspec1(type) { + if (type == "var") return cont(vardef1, forspec2); + if (type == ";") return pass(forspec2); + if (type == "variable") return cont(formaybein); + return pass(forspec2); + } + function formaybein(type, value) { + if (value == "in") return cont(expression); + return cont(maybeoperator, forspec2); + } + function forspec2(type, value) { + if (type == ";") return cont(forspec3); + if (value == "in") return cont(expression); + return cont(expression, expect(";"), forspec3); + } + function forspec3(type) { + if (type != ")") cont(expression); + } + function functiondef(type, value) { + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); + } + function funarg(type, value) { + if (type == "variable") {register(value); return cont();} + } + + // Interface + + return { + startState: function(basecolumn) { + return { + tokenize: jsTokenBase, + reAllowed: true, + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: null, + context: null, + indented: 0 + }; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + } + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/); + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize != jsTokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, + type = lexical.type, closing = firstChar == type; + if (type == "vardef") return lexical.indented + 4; + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "stat" || type == "form") return lexical.indented + indentUnit; + else if (lexical.info == "switch" && !closing) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricChars: ":{}" + }; +}); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/controls.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/controls.js new file mode 100644 index 00000000000..ca29aefdd1f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/controls.js @@ -0,0 +1,963 @@ +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2008 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 = Class.create({ + 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; + this.oldElementValue = this.element.value; + + 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); + // Force carriage returns as token delimiters anyway + if (!this.options.tokens.include('\n')) + this.options.tokens.push('\n'); + + 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, 'keydown', this.onKeyPress.bindAsEventListener(this)); + }, + + 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(); + Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + 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 = $(selectedElement).select('.' + this.options.select) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var bounds = this.getTokenBounds(); + if (bounds[0] != -1) { + var newValue = this.element.value.substr(0, bounds[0]); + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value + this.element.value.substr(bounds[1]); + } else { + this.element.value = value; + } + this.oldElementValue = this.element.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; + this.tokenBounds = null; + if(this.getToken().length>=this.options.minChars) { + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + this.oldElementValue = this.element.value; + }, + + getToken: function() { + var bounds = this.getTokenBounds(); + return this.element.value.substring(bounds[0], bounds[1]).strip(); + }, + + getTokenBounds: function() { + if (null != this.tokenBounds) return this.tokenBounds; + var value = this.element.value; + if (value.strip().empty()) return [-1, 0]; + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); + var offset = (diff == this.oldElementValue.length ? 1 : 0); + var prevTokenPos = -1, nextTokenPos = value.length; + var tp; + for (var index = 0, l = this.options.tokens.length; index < l; ++index) { + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); + if (tp > prevTokenPos) prevTokenPos = tp; + tp = value.indexOf(this.options.tokens[index], diff + offset); + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; + } + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); + } +}); + +Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { + var boundary = Math.min(newS.length, oldS.length); + for (var index = 0; index < boundary; ++index) + if (newS[index] != oldS[index]) + return index; + return boundary; +}; + +Ajax.Autocompleter = Class.create(Autocompleter.Base, { + 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.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 and collection editor +// Full rewrite by Christophe Porteneuve (April 2007). + +// 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({ + initialize: function(element, url, options) { + this.url = url; + this.element = element = $(element); + this.prepareOptions(); + this._controls = { }; + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! + Object.extend(this.options, options || { }); + if (!this.options.formId && this.element.id) { + this.options.formId = this.element.id + '-inplaceeditor'; + if ($(this.options.formId)) + this.options.formId = ''; + } + if (this.options.externalControl) + this.options.externalControl = $(this.options.externalControl); + if (!this.options.externalControl) + this.options.externalControlOnly = false; + this._originalBackground = this.element.getStyle('background-color') || 'transparent'; + this.element.title = this.options.clickToEditText; + this._boundCancelHandler = this.handleFormCancellation.bind(this); + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); + this._boundFailureHandler = this.handleAJAXFailure.bind(this); + this._boundSubmitHandler = this.handleFormSubmission.bind(this); + this._boundWrapperHandler = this.wrapUp.bind(this); + this.registerListeners(); + }, + checkForEscapeOrReturn: function(e) { + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; + if (Event.KEY_ESC == e.keyCode) + this.handleFormCancellation(e); + else if (Event.KEY_RETURN == e.keyCode) + this.handleFormSubmission(e); + }, + createControl: function(mode, handler, extraClasses) { + var control = this.options[mode + 'Control']; + var text = this.options[mode + 'Text']; + if ('button' == control) { + var btn = document.createElement('input'); + btn.type = 'submit'; + btn.value = text; + btn.className = 'editor_' + mode + '_button'; + if ('cancel' == mode) + btn.onclick = this._boundCancelHandler; + this._form.appendChild(btn); + this._controls[mode] = btn; + } else if ('link' == control) { + var link = document.createElement('a'); + link.href = '#'; + link.appendChild(document.createTextNode(text)); + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; + link.className = 'editor_' + mode + '_link'; + if (extraClasses) + link.className += ' ' + extraClasses; + this._form.appendChild(link); + this._controls[mode] = link; + } + }, + createEditField: function() { + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); + var fld; + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { + fld = document.createElement('input'); + fld.type = 'text'; + var size = this.options.size || this.options.cols || 0; + if (0 < size) fld.size = size; + } else { + fld = document.createElement('textarea'); + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); + fld.cols = this.options.cols || 40; + } + fld.name = this.options.paramName; + fld.value = text; // No HTML breaks conversion anymore + fld.className = 'editor_field'; + if (this.options.submitOnBlur) + fld.onblur = this._boundSubmitHandler; + this._controls.editor = fld; + if (this.options.loadTextURL) + this.loadExternalText(); + this._form.appendChild(this._controls.editor); + }, + createForm: function() { + var ipe = this; + function addText(mode, condition) { + var text = ipe.options['text' + mode + 'Controls']; + if (!text || condition === false) return; + ipe._form.appendChild(document.createTextNode(text)); + }; + this._form = $(document.createElement('form')); + this._form.id = this.options.formId; + this._form.addClassName(this.options.formClassName); + this._form.onsubmit = this._boundSubmitHandler; + this.createEditField(); + if ('textarea' == this._controls.editor.tagName.toLowerCase()) + this._form.appendChild(document.createElement('br')); + if (this.options.onFormCustomization) + this.options.onFormCustomization(this, this._form); + addText('Before', this.options.okControl || this.options.cancelControl); + this.createControl('ok', this._boundSubmitHandler); + addText('Between', this.options.okControl && this.options.cancelControl); + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); + addText('After', this.options.okControl || this.options.cancelControl); + }, + destroy: function() { + if (this._oldInnerHTML) + this.element.innerHTML = this._oldInnerHTML; + this.leaveEditMode(); + this.unregisterListeners(); + }, + enterEditMode: function(e) { + if (this._saving || this._editing) return; + this._editing = true; + this.triggerCallback('onEnterEditMode'); + if (this.options.externalControl) + this.options.externalControl.hide(); + this.element.hide(); + this.createForm(); + this.element.parentNode.insertBefore(this._form, this.element); + if (!this.options.loadTextURL) + this.postProcessEditField(); + if (e) Event.stop(e); + }, + enterHover: function(e) { + if (this.options.hoverClassName) + this.element.addClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onEnterHover'); + }, + getText: function() { + return this.element.innerHTML.unescapeHTML(); + }, + handleAJAXFailure: function(transport) { + this.triggerCallback('onFailure', transport); + if (this._oldInnerHTML) { + this.element.innerHTML = this._oldInnerHTML; + this._oldInnerHTML = null; + } + }, + handleFormCancellation: function(e) { + this.wrapUp(); + if (e) Event.stop(e); + }, + handleFormSubmission: function(e) { + var form = this._form; + var value = $F(this._controls.editor); + this.prepareSubmission(); + var params = this.options.callback(form, value) || ''; + if (Object.isString(params)) + params = params.toQueryParams(); + params.editorId = this.element.id; + if (this.options.htmlResponse) { + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Updater({ success: this.element }, this.url, options); + } else { + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.url, options); + } + if (e) Event.stop(e); + }, + leaveEditMode: function() { + this.element.removeClassName(this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + if (this.options.externalControl) + this.options.externalControl.show(); + this._saving = false; + this._editing = false; + this._oldInnerHTML = null; + this.triggerCallback('onLeaveEditMode'); + }, + leaveHover: function(e) { + if (this.options.hoverClassName) + this.element.removeClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onLeaveHover'); + }, + loadExternalText: function() { + this._form.addClassName(this.options.loadingClassName); + this._controls.editor.disabled = true; + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._form.removeClassName(this.options.loadingClassName); + var text = transport.responseText; + if (this.options.stripLoadedTextTags) + text = text.stripTags(); + this._controls.editor.value = text; + this._controls.editor.disabled = false; + this.postProcessEditField(); + }.bind(this), + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + postProcessEditField: function() { + var fpc = this.options.fieldPostCreation; + if (fpc) + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); + }, + prepareOptions: function() { + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); + [this._extraDefaultOptions].flatten().compact().each(function(defs) { + Object.extend(this.options, defs); + }.bind(this)); + }, + prepareSubmission: function() { + this._saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + registerListeners: function() { + this._listeners = { }; + var listener; + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { + listener = this[pair.value].bind(this); + this._listeners[pair.key] = listener; + if (!this.options.externalControlOnly) + this.element.observe(pair.key, listener); + if (this.options.externalControl) + this.options.externalControl.observe(pair.key, listener); + }.bind(this)); + }, + removeForm: function() { + if (!this._form) return; + this._form.remove(); + this._form = null; + this._controls = { }; + }, + showSaving: function() { + this._oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + this.element.addClassName(this.options.savingClassName); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + }, + triggerCallback: function(cbName, arg) { + if ('function' == typeof this.options[cbName]) { + this.options[cbName](this, arg); + } + }, + unregisterListeners: function() { + $H(this._listeners).each(function(pair) { + if (!this.options.externalControlOnly) + this.element.stopObserving(pair.key, pair.value); + if (this.options.externalControl) + this.options.externalControl.stopObserving(pair.key, pair.value); + }.bind(this)); + }, + wrapUp: function(transport) { + this.leaveEditMode(); + // Can't use triggerCallback due to backward compatibility: requires + // binding + direct element + this._boundComplete(transport, this.element); + } +}); + +Object.extend(Ajax.InPlaceEditor.prototype, { + dispose: Ajax.InPlaceEditor.prototype.destroy +}); + +Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { + initialize: function($super, element, url, options) { + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; + $super(element, url, options); + }, + + createEditField: function() { + var list = document.createElement('select'); + list.name = this.options.paramName; + list.size = 1; + this._controls.editor = list; + this._collection = this.options.collection || []; + if (this.options.loadCollectionURL) + this.loadCollection(); + else + this.checkForExternalText(); + this._form.appendChild(this._controls.editor); + }, + + loadCollection: function() { + this._form.addClassName(this.options.loadingClassName); + this.showLoadingText(this.options.loadingCollectionText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + var js = transport.responseText.strip(); + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check + throw('Server returned an invalid collection representation.'); + this._collection = eval(js); + this.checkForExternalText(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadCollectionURL, options); + }, + + showLoadingText: function(text) { + this._controls.editor.disabled = true; + var tempOption = this._controls.editor.firstChild; + if (!tempOption) { + tempOption = document.createElement('option'); + tempOption.value = ''; + this._controls.editor.appendChild(tempOption); + tempOption.selected = true; + } + tempOption.update((text || '').stripScripts().stripTags()); + }, + + checkForExternalText: function() { + this._text = this.getText(); + if (this.options.loadTextURL) + this.loadExternalText(); + else + this.buildOptionList(); + }, + + loadExternalText: function() { + this.showLoadingText(this.options.loadingText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._text = transport.responseText.strip(); + this.buildOptionList(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + + buildOptionList: function() { + this._form.removeClassName(this.options.loadingClassName); + this._collection = this._collection.map(function(entry) { + return 2 === entry.length ? entry : [entry, entry].flatten(); + }); + var marker = ('value' in this.options) ? this.options.value : this._text; + var textFound = this._collection.any(function(entry) { + return entry[0] == marker; + }.bind(this)); + this._controls.editor.update(''); + var option; + this._collection.each(function(entry, index) { + option = document.createElement('option'); + option.value = entry[0]; + option.selected = textFound ? entry[0] == marker : 0 == index; + option.appendChild(document.createTextNode(entry[1])); + this._controls.editor.appendChild(option); + }.bind(this)); + this._controls.editor.disabled = false; + Field.scrollFreeActivate(this._controls.editor); + } +}); + +//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** +//**** This only exists for a while, in order to let **** +//**** users adapt to the new API. Read up on the new **** +//**** API and convert your code to it ASAP! **** + +Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { + if (!options) return; + function fallback(name, expr) { + if (name in options || expr === undefined) return; + options[name] = expr; + }; + fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : + options.cancelLink == options.cancelButton == false ? false : undefined))); + fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : + options.okLink == options.okButton == false ? false : undefined))); + fallback('highlightColor', options.highlightcolor); + fallback('highlightEndColor', options.highlightendcolor); +}; + +Object.extend(Ajax.InPlaceEditor, { + DefaultOptions: { + ajaxOptions: { }, + autoRows: 3, // Use when multi-line w/ rows == 1 + cancelControl: 'link', // 'link'|'button'|false + cancelText: 'cancel', + clickToEditText: 'Click to edit', + externalControl: null, // id|elt + externalControlOnly: false, + fieldPostCreation: 'activate', // 'activate'|'focus'|false + formClassName: 'inplaceeditor-form', + formId: null, // id|elt + highlightColor: '#ffff99', + highlightEndColor: '#ffffff', + hoverClassName: '', + htmlResponse: true, + loadingClassName: 'inplaceeditor-loading', + loadingText: 'Loading...', + okControl: 'button', // 'link'|'button'|false + okText: 'ok', + paramName: 'value', + rows: 1, // If 1 and multi-line, uses autoRows + savingClassName: 'inplaceeditor-saving', + savingText: 'Saving...', + size: 0, + stripLoadedTextTags: false, + submitOnBlur: false, + textAfterControls: '', + textBeforeControls: '', + textBetweenControls: '' + }, + DefaultCallbacks: { + callback: function(form) { + return Form.serialize(form); + }, + onComplete: function(transport, element) { + // For backward compatibility, this one is bound to the IPE, and passes + // the element directly. It was too often customized, so we don't break it. + new Effect.Highlight(element, { + startcolor: this.options.highlightColor, keepBackgroundImage: true }); + }, + onEnterEditMode: null, + onEnterHover: function(ipe) { + ipe.element.style.backgroundColor = ipe.options.highlightColor; + if (ipe._effect) + ipe._effect.cancel(); + }, + onFailure: function(transport, ipe) { + alert('Error communication with the server: ' + transport.responseText.stripTags()); + }, + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. + onLeaveEditMode: null, + onLeaveHover: function(ipe) { + ipe._effect = new Effect.Highlight(ipe.element, { + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, + restorecolor: ipe._originalBackground, keepBackgroundImage: true + }); + } + }, + Listeners: { + click: 'enterEditMode', + keydown: 'checkForEscapeOrReturn', + mouseover: 'enterHover', + mouseout: 'leaveHover' + } +}); + +Ajax.InPlaceCollectionEditor.DefaultOptions = { + loadingCollectionText: 'Loading options...' +}; + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create({ + 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)); + } +}); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/dragdrop.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/dragdrop.js new file mode 100644 index 00000000000..07229f986f5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/dragdrop.js @@ -0,0 +1,973 @@ +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 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(Object.isUndefined(Effect)) + 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(Object.isArray(containment)) { + 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 drop, affected = []; + + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) + drop = Droppables.findDeepestChild(affected); + + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); + if (drop) { + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + if (drop != this.last_active) 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({ + 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 = Object.isNumber(element._opacity) ? 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] || Object.isUndefined(arguments[1].endeffect)) + 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 && Object.isString(options.handle)) + 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.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(!Object.isUndefined(Draggable._dragging[this.element]) && + 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.delta) + this.delta = this.currentDelta(); + + 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); + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) + 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) { + if (!this._originallyAbsolute) + Position.relativize(this.element); + delete this._originallyAbsolute; + 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 && Object.isFunction(revert)) 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(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*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 }; + } +}); + +Draggable._dragging = { }; + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create({ + 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){ + element = $(element); + var s = Sortable.sortables[element.id]; + + 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).select('.' + 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')]; +}; \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/effects.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/effects.js new file mode 100644 index 00000000000..5a639d2dea9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/effects.js @@ -0,0 +1,1128 @@ +// Copyright (c) 2005-2008 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) { } +}; + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + .5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; + }, + pulse: function(pos, pulses) { + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, + tagifyText: function(element) { + 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( + new Element('span', {style: tagifyStyle}).update( + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if (((typeof element == 'object') || + Object.isFunction(element)) && + (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); + } +}; + +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(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 = Object.isString(effect.options.queue) ? + 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 = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + '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 (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#'; + } +}); + +Effect.Parallel = Class.create(Effect.Base, { + 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.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { + 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(Effect.Base, { + 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() { + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if (this.options.mode == 'absolute') { + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + '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(Effect.Base, { + 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 = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + '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(Effect.Base, { + 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+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()); } + ); +}; + +/* ------------- 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 options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance, y: 0, duration: split, 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'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + 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().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, 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] || { }, + oldOpacity = element.getInlineOpacity(), + transition = options.transition || Effect.Transitions.linear, + reverser = function(pos){ + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); + }; + + 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(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.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 + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: 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 ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, 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.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '
    '; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); + + return styleRules; +}; + +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { + results[property] = css[property]; + return results; + }); + if (!styles.opacity) styles.opacity = element.getOpacity(); + return styles; + }; +} + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element); + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + }; + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/html5.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/html5.js new file mode 100644 index 00000000000..e6c93e41314 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/html5.js @@ -0,0 +1,4 @@ +// html5shiv @rem remysharp.com/html5-enabling-script +// iepp v1.6.2 @jon_neal iecss.com/print-protector +// Dual licensed under the MIT or GPL Version 2 licenses +/*@cc_on(function(a,b){function r(a){var b=-1;while(++b";return a.childNodes.length!==1}())){a.iepp=a.iepp||{};var c=a.iepp,d=c.html5elements||"abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",e=d.split("|"),f=e.length,g=new RegExp("(^|\\s)("+d+")","gi"),h=new RegExp("<(/*)("+d+")","gi"),i=/^\s*[\{\}]\s*$/,j=new RegExp("(^|[^\\n]*?\\s)("+d+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),k=b.createDocumentFragment(),l=b.documentElement,m=l.firstChild,n=b.createElement("body"),o=b.createElement("style"),p=/print|all/,q;c.getCSS=function(a,b){if(a+""===undefined)return"";var d=-1,e=a.length,f,g=[];while(++d
    + value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + }, + + disableSelection: function() { + return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }, + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + } +}); + +$.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0; + if ( border ) { + size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; +}); + +// selectors +function visible( element ) { + return !$( element ).parents().andSelf().filter(function() { + return $.curCSS( this, "visibility" ) === "hidden" || + $.expr.filters.hidden( this ); + }).length; +} + +$.extend( $.expr[ ":" ], { + data: function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + var nodeName = element.nodeName.toLowerCase(), + tabIndex = $.attr( element, "tabindex" ); + if ( "area" === nodeName ) { + var map = element.parentNode, + mapName = map.name, + img; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap=#" + mapName + "]" )[0]; + return !!img && visible( img ); + } + return ( /input|select|textarea|button|object/.test( nodeName ) + ? !element.disabled + : "a" == nodeName + ? element.href || !isNaN( tabIndex ) + : !isNaN( tabIndex )) + // the element and all of its ancestors must be visible + && visible( element ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ); + return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" ); + } +}); + +// support +$(function() { + var body = document.body, + div = body.appendChild( div = document.createElement( "div" ) ); + + $.extend( div.style, { + minHeight: "100px", + height: "auto", + padding: 0, + borderWidth: 0 + }); + + $.support.minHeight = div.offsetHeight === 100; + $.support.selectstart = "onselectstart" in div; + + // set display to none to avoid a layout bug in IE + // http://dev.jquery.com/ticket/4014 + body.removeChild( div ).style.display = "none"; +}); + + + + + +// deprecated +$.extend( $.ui, { + // $.ui.plugin is deprecated. Use the proxy pattern instead. + plugin: { + add: function( module, option, set ) { + var proto = $.ui[ module ].prototype; + for ( var i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args ) { + var set = instance.plugins[ name ]; + if ( !set || !instance.element[ 0 ].parentNode ) { + return; + } + + for ( var i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } + }, + + // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains() + contains: function( a, b ) { + return document.compareDocumentPosition ? + a.compareDocumentPosition( b ) & 16 : + a !== b && a.contains( b ); + }, + + // only used by resizable + hasScroll: function( el, a ) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ( $( el ).css( "overflow" ) === "hidden") { + return false; + } + + var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", + has = false; + + if ( el[ scroll ] > 0 ) { + return true; + } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[ scroll ] = 1; + has = ( el[ scroll ] > 0 ); + el[ scroll ] = 0; + return has; + }, + + // these are odd functions, fix the API or move into individual plugins + isOverAxis: function( x, reference, size ) { + //Determines when x coordinate is over "b" element axis + return ( x > reference ) && ( x < ( reference + size ) ); + }, + isOver: function( y, x, top, left, height, width ) { + //Determines when x, y coordinates is over "b" element + return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width ); + } +}); + +})( jQuery ); +/*! + * jQuery UI Widget 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Widget + */ +(function( $, undefined ) { + +// jQuery 1.4+ +if ( $.cleanData ) { + var _cleanData = $.cleanData; + $.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + $( elem ).triggerHandler( "remove" ); + } + _cleanData( elems ); + }; +} else { + var _remove = $.fn.remove; + $.fn.remove = function( selector, keepData ) { + return this.each(function() { + if ( !keepData ) { + if ( !selector || $.filter( selector, [ this ] ).length ) { + $( "*", this ).add( [ this ] ).each(function() { + $( this ).triggerHandler( "remove" ); + }); + } + } + return _remove.call( $(this), selector, keepData ); + }); + }; +} + +$.widget = function( name, base, prototype ) { + var namespace = name.split( "." )[ 0 ], + fullName; + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName ] = function( elem ) { + return !!$.data( elem, name ); + }; + + $[ namespace ] = $[ namespace ] || {}; + $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + var basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from +// $.each( basePrototype, function( key, val ) { +// if ( $.isPlainObject(val) ) { +// basePrototype[ key ] = $.extend( {}, val ); +// } +// }); + basePrototype.options = $.extend( true, {}, basePrototype.options ); + $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + namespace: namespace, + widgetName: name, + widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, + widgetBaseClass: fullName + }, prototype ); + + $.widget.bridge( name, $[ namespace ][ name ] ); +}; + +$.widget.bridge = function( name, object ) { + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = Array.prototype.slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.extend.apply( null, [ true, options ].concat(args) ) : + options; + + // prevent calls to internal methods + if ( isMethodCall && options.charAt( 0 ) === "_" ) { + return returnValue; + } + + if ( isMethodCall ) { + this.each(function() { + var instance = $.data( this, name ), + methodValue = instance && $.isFunction( instance[options] ) ? + instance[ options ].apply( instance, args ) : + instance; + // TODO: add this back in 1.9 and use $.error() (see #5972) +// if ( !instance ) { +// throw "cannot call methods on " + name + " prior to initialization; " + +// "attempted to call method '" + options + "'"; +// } +// if ( !$.isFunction( instance[options] ) ) { +// throw "no such method '" + options + "' for " + name + " widget instance"; +// } +// var methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, name ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, name, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } +}; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + options: { + disabled: false + }, + _createWidget: function( options, element ) { + // $.widget.bridge stores the plugin instance, but we do it anyway + // so that it's stored even before the _create function runs + $.data( element, this.widgetName, this ); + this.element = $( element ); + this.options = $.extend( true, {}, + this.options, + this._getCreateOptions(), + options ); + + var self = this; + this.element.bind( "remove." + this.widgetName, function() { + self.destroy(); + }); + + this._create(); + this._trigger( "create" ); + this._init(); + }, + _getCreateOptions: function() { + return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; + }, + _create: function() {}, + _init: function() {}, + + destroy: function() { + this.element + .unbind( "." + this.widgetName ) + .removeData( this.widgetName ); + this.widget() + .unbind( "." + this.widgetName ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetBaseClass + "-disabled " + + "ui-state-disabled" ); + }, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.extend( {}, this.options ); + } + + if (typeof key === "string" ) { + if ( value === undefined ) { + return this.options[ key ]; + } + options = {}; + options[ key ] = value; + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var self = this; + $.each( options, function( key, value ) { + self._setOption( key, value ); + }); + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + [ value ? "addClass" : "removeClass"]( + this.widgetBaseClass + "-disabled" + " " + + "ui-state-disabled" ) + .attr( "aria-disabled", value ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _trigger: function( type, event, data ) { + var callback = this.options[ type ]; + + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + data = data || {}; + + // copy original event properties over to the new event + // this would happen if we could call $.event.fix instead of $.Event + // but we don't have a way to force an event to be fixed multiple times + if ( event.originalEvent ) { + for ( var i = $.event.props.length, prop; i; ) { + prop = $.event.props[ --i ]; + event[ prop ] = event.originalEvent[ prop ]; + } + } + + this.element.trigger( event, data ); + + return !( $.isFunction(callback) && + callback.call( this.element[0], event, data ) === false || + event.isDefaultPrevented() ); + } +}; + +})( jQuery ); +/*! + * jQuery UI Mouse 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Mouse + * + * Depends: + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget("ui.mouse", { + options: { + cancel: ':input,option', + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var self = this; + + this.element + .bind('mousedown.'+this.widgetName, function(event) { + return self._mouseDown(event); + }) + .bind('click.'+this.widgetName, function(event) { + if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) { + $.removeData(event.target, self.widgetName + '.preventClickEvent'); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind('.'+this.widgetName); + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + // TODO: figure out why we have to use originalEvent + event.originalEvent = event.originalEvent || {}; + if (event.originalEvent.mouseHandled) { return; } + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var self = this, + btnIsLeft = (event.which == 1), + elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + self.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) { + $.removeData(event.target, this.widgetName + '.preventClickEvent'); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return self._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return self._mouseUp(event); + }; + $(document) + .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + event.preventDefault(); + event.originalEvent.mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.browser.msie && !(document.documentMode >= 9) && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target == this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + '.preventClickEvent', true); + } + + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(event) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(event) {}, + _mouseDrag: function(event) {}, + _mouseStop: function(event) {}, + _mouseCapture: function(event) { return true; } +}); + +})(jQuery); +/* + * jQuery UI Draggable 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget("ui.draggable", $.ui.mouse, { + widgetEventPrefix: "drag", + options: { + addClasses: true, + appendTo: "parent", + axis: false, + connectToSortable: false, + containment: false, + cursor: "auto", + cursorAt: false, + grid: false, + handle: false, + helper: "original", + iframeFix: false, + opacity: false, + refreshPositions: false, + revert: false, + revertDuration: 500, + scope: "default", + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: false + }, + _create: function() { + + if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position"))) + this.element[0].style.position = 'relative'; + + (this.options.addClasses && this.element.addClass("ui-draggable")); + (this.options.disabled && this.element.addClass("ui-draggable-disabled")); + + this._mouseInit(); + + }, + + destroy: function() { + if(!this.element.data('draggable')) return; + this.element + .removeData("draggable") + .unbind(".draggable") + .removeClass("ui-draggable" + + " ui-draggable-dragging" + + " ui-draggable-disabled"); + this._mouseDestroy(); + + return this; + }, + + _mouseCapture: function(event) { + + var o = this.options; + + // among others, prevent a drag on a resizable-handle + if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle')) + return false; + + //Quit if we're not on a valid handle + this.handle = this._getHandle(event); + if (!this.handle) + return false; + + return true; + + }, + + _mouseStart: function(event) { + + var o = this.options; + + //Create and append the visible helper + this.helper = this._createHelper(event); + + //Cache the helper size + this._cacheHelperProportions(); + + //If ddmanager is used for droppables, set the global draggable + if($.ui.ddmanager) + $.ui.ddmanager.current = this; + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Store the helper's css position + this.cssPosition = this.helper.css("position"); + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.positionAbs = this.element.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + $.extend(this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper + }); + + //Generate the original position + this.originalPosition = this.position = this._generatePosition(event); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied + (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); + + //Set a containment if given in the options + if(o.containment) + this._setContainment(); + + //Trigger event + callbacks + if(this._trigger("start", event) === false) { + this._clear(); + return false; + } + + //Recache the helper size + this._cacheHelperProportions(); + + //Prepare the droppable offsets + if ($.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(this, event); + + this.helper.addClass("ui-draggable-dragging"); + this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position + return true; + }, + + _mouseDrag: function(event, noPropagation) { + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + //Call plugins and callbacks and use the resulting position if something is returned + if (!noPropagation) { + var ui = this._uiHash(); + if(this._trigger('drag', event, ui) === false) { + this._mouseUp({}); + return false; + } + this.position = ui.position; + } + + if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px'; + if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px'; + if($.ui.ddmanager) $.ui.ddmanager.drag(this, event); + + return false; + }, + + _mouseStop: function(event) { + + //If we are using droppables, inform the manager about the drop + var dropped = false; + if ($.ui.ddmanager && !this.options.dropBehaviour) + dropped = $.ui.ddmanager.drop(this, event); + + //if a drop comes from outside (a sortable) + if(this.dropped) { + dropped = this.dropped; + this.dropped = false; + } + + //if the original element is removed, don't bother to continue if helper is set to "original" + if((!this.element[0] || !this.element[0].parentNode) && this.options.helper == "original") + return false; + + if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { + var self = this; + $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { + if(self._trigger("stop", event) !== false) { + self._clear(); + } + }); + } else { + if(this._trigger("stop", event) !== false) { + this._clear(); + } + } + + return false; + }, + + cancel: function() { + + if(this.helper.is(".ui-draggable-dragging")) { + this._mouseUp({}); + } else { + this._clear(); + } + + return this; + + }, + + _getHandle: function(event) { + + var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false; + $(this.options.handle, this.element) + .find("*") + .andSelf() + .each(function() { + if(this == event.target) handle = true; + }); + + return handle; + + }, + + _createHelper: function(event) { + + var o = this.options; + var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element); + + if(!helper.parents('body').length) + helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); + + if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) + helper.css("position", "absolute"); + + return helper; + + }, + + _adjustOffsetFromHelper: function(obj) { + if (typeof obj == 'string') { + obj = obj.split(' '); + } + if ($.isArray(obj)) { + obj = {left: +obj[0], top: +obj[1] || 0}; + } + if ('left' in obj) { + this.offset.click.left = obj.left + this.margins.left; + } + if ('right' in obj) { + this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + } + if ('top' in obj) { + this.offset.click.top = obj.top + this.margins.top; + } + if ('bottom' in obj) { + this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + } + }, + + _getParentOffset: function() { + + //Get the offsetParent and cache its position + this.offsetParent = this.helper.offsetParent(); + var po = this.offsetParent.offset(); + + // This is a special case where we need to modify a offset calculated on start, since the following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that + // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag + if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information + || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix + po = { top: 0, left: 0 }; + + return { + top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), + left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) + }; + + }, + + _getRelativeOffset: function() { + + if(this.cssPosition == "relative") { + var p = this.element.position(); + return { + top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), + left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: (parseInt(this.element.css("marginLeft"),10) || 0), + top: (parseInt(this.element.css("marginTop"),10) || 0), + right: (parseInt(this.element.css("marginRight"),10) || 0), + bottom: (parseInt(this.element.css("marginBottom"),10) || 0) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var o = this.options; + if(o.containment == 'parent') o.containment = this.helper[0].parentNode; + if(o.containment == 'document' || o.containment == 'window') this.containment = [ + (o.containment == 'document' ? 0 : $(window).scrollLeft()) - this.offset.relative.left - this.offset.parent.left, + (o.containment == 'document' ? 0 : $(window).scrollTop()) - this.offset.relative.top - this.offset.parent.top, + (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left, + (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top + ]; + + if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) { + var ce = $(o.containment)[0]; if(!ce) return; + var co = $(o.containment).offset(); + var over = ($(ce).css("overflow") != 'hidden'); + + this.containment = [ + co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0), + co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0), + co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right, + co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom + ]; + } else if(o.containment.constructor == Array) { + this.containment = o.containment; + } + + }, + + _convertPositionTo: function(d, pos) { + + if(!pos) pos = this.position; + var mod = d == "absolute" ? 1 : -1; + var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + return { + top: ( + pos.top // The absolute mouse position + + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border) + - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) + ), + left: ( + pos.left // The absolute mouse position + + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border) + - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) + ) + }; + + }, + + _generatePosition: function(event) { + + var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + var pageX = event.pageX; + var pageY = event.pageY; + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + if(this.originalPosition) { //If we are not dragging yet, we won't check for options + + if(this.containment) { + if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left; + if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top; + if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left; + if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top; + } + + if(o.grid) { + var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; + pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; + + var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; + pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; + } + + } + + return { + top: ( + pageY // The absolute mouse position + - this.offset.click.top // Click offset (relative to the element) + - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.top // The offsetParent's offset without borders (offset + border) + + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) + ), + left: ( + pageX // The absolute mouse position + - this.offset.click.left // Click offset (relative to the element) + - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.left // The offsetParent's offset without borders (offset + border) + + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) + ) + }; + + }, + + _clear: function() { + this.helper.removeClass("ui-draggable-dragging"); + if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove(); + //if($.ui.ddmanager) $.ui.ddmanager.current = null; + this.helper = null; + this.cancelHelperRemoval = false; + }, + + // From now on bulk stuff - mainly helpers + + _trigger: function(type, event, ui) { + ui = ui || this._uiHash(); + $.ui.plugin.call(this, type, [event, ui]); + if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins + return $.Widget.prototype._trigger.call(this, type, event, ui); + }, + + plugins: {}, + + _uiHash: function(event) { + return { + helper: this.helper, + position: this.position, + originalPosition: this.originalPosition, + offset: this.positionAbs + }; + } + +}); + +$.extend($.ui.draggable, { + version: "1.8.12" +}); + +$.ui.plugin.add("draggable", "connectToSortable", { + start: function(event, ui) { + + var inst = $(this).data("draggable"), o = inst.options, + uiSortable = $.extend({}, ui, { item: inst.element }); + inst.sortables = []; + $(o.connectToSortable).each(function() { + var sortable = $.data(this, 'sortable'); + if (sortable && !sortable.options.disabled) { + inst.sortables.push({ + instance: sortable, + shouldRevert: sortable.options.revert + }); + sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page). + sortable._trigger("activate", event, uiSortable); + } + }); + + }, + stop: function(event, ui) { + + //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper + var inst = $(this).data("draggable"), + uiSortable = $.extend({}, ui, { item: inst.element }); + + $.each(inst.sortables, function() { + if(this.instance.isOver) { + + this.instance.isOver = 0; + + inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance + this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) + + //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid' + if(this.shouldRevert) this.instance.options.revert = true; + + //Trigger the stop of the sortable + this.instance._mouseStop(event); + + this.instance.options.helper = this.instance.options._helper; + + //If the helper has been the original item, restore properties in the sortable + if(inst.options.helper == 'original') + this.instance.currentItem.css({ top: 'auto', left: 'auto' }); + + } else { + this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance + this.instance._trigger("deactivate", event, uiSortable); + } + + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("draggable"), self = this; + + var checkPos = function(o) { + var dyClick = this.offset.click.top, dxClick = this.offset.click.left; + var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left; + var itemHeight = o.height, itemWidth = o.width; + var itemTop = o.top, itemLeft = o.left; + + return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth); + }; + + $.each(inst.sortables, function(i) { + + //Copy over some variables to allow calling the sortable's native _intersectsWith + this.instance.positionAbs = inst.positionAbs; + this.instance.helperProportions = inst.helperProportions; + this.instance.offset.click = inst.offset.click; + + if(this.instance._intersectsWith(this.instance.containerCache)) { + + //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once + if(!this.instance.isOver) { + + this.instance.isOver = 1; + //Now we fake the start of dragging for the sortable instance, + //by cloning the list group item, appending it to the sortable and using it as inst.currentItem + //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) + this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true); + this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it + this.instance.options.helper = function() { return ui.helper[0]; }; + + event.target = this.instance.currentItem[0]; + this.instance._mouseCapture(event, true); + this.instance._mouseStart(event, true, true); + + //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes + this.instance.offset.click.top = inst.offset.click.top; + this.instance.offset.click.left = inst.offset.click.left; + this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; + this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; + + inst._trigger("toSortable", event); + inst.dropped = this.instance.element; //draggable revert needs that + //hack so receive/update callbacks work (mostly) + inst.currentItem = inst.element; + this.instance.fromOutside = inst; + + } + + //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable + if(this.instance.currentItem) this.instance._mouseDrag(event); + + } else { + + //If it doesn't intersect with the sortable, and it intersected before, + //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval + if(this.instance.isOver) { + + this.instance.isOver = 0; + this.instance.cancelHelperRemoval = true; + + //Prevent reverting on this forced stop + this.instance.options.revert = false; + + // The out event needs to be triggered independently + this.instance._trigger('out', event, this.instance._uiHash(this.instance)); + + this.instance._mouseStop(event, true); + this.instance.options.helper = this.instance.options._helper; + + //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size + this.instance.currentItem.remove(); + if(this.instance.placeholder) this.instance.placeholder.remove(); + + inst._trigger("fromSortable", event); + inst.dropped = false; //draggable revert needs that + } + + }; + + }); + + } +}); + +$.ui.plugin.add("draggable", "cursor", { + start: function(event, ui) { + var t = $('body'), o = $(this).data('draggable').options; + if (t.css("cursor")) o._cursor = t.css("cursor"); + t.css("cursor", o.cursor); + }, + stop: function(event, ui) { + var o = $(this).data('draggable').options; + if (o._cursor) $('body').css("cursor", o._cursor); + } +}); + +$.ui.plugin.add("draggable", "iframeFix", { + start: function(event, ui) { + var o = $(this).data('draggable').options; + $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { + $('
    ') + .css({ + width: this.offsetWidth+"px", height: this.offsetHeight+"px", + position: "absolute", opacity: "0.001", zIndex: 1000 + }) + .css($(this).offset()) + .appendTo("body"); + }); + }, + stop: function(event, ui) { + $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers + } +}); + +$.ui.plugin.add("draggable", "opacity", { + start: function(event, ui) { + var t = $(ui.helper), o = $(this).data('draggable').options; + if(t.css("opacity")) o._opacity = t.css("opacity"); + t.css('opacity', o.opacity); + }, + stop: function(event, ui) { + var o = $(this).data('draggable').options; + if(o._opacity) $(ui.helper).css('opacity', o._opacity); + } +}); + +$.ui.plugin.add("draggable", "scroll", { + start: function(event, ui) { + var i = $(this).data("draggable"); + if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset(); + }, + drag: function(event, ui) { + + var i = $(this).data("draggable"), o = i.options, scrolled = false; + + if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') { + + if(!o.axis || o.axis != 'x') { + if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; + else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; + } + + if(!o.axis || o.axis != 'y') { + if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; + else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; + } + + } else { + + if(!o.axis || o.axis != 'x') { + if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); + else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); + } + + if(!o.axis || o.axis != 'y') { + if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); + else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); + } + + } + + if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(i, event); + + } +}); + +$.ui.plugin.add("draggable", "snap", { + start: function(event, ui) { + + var i = $(this).data("draggable"), o = i.options; + i.snapElements = []; + + $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() { + var $t = $(this); var $o = $t.offset(); + if(this != i.element[0]) i.snapElements.push({ + item: this, + width: $t.outerWidth(), height: $t.outerHeight(), + top: $o.top, left: $o.left + }); + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("draggable"), o = inst.options; + var d = o.snapTolerance; + + var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, + y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; + + for (var i = inst.snapElements.length - 1; i >= 0; i--){ + + var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width, + t = inst.snapElements[i].top, b = t + inst.snapElements[i].height; + + //Yes, I know, this is insane ;) + if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) { + if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + inst.snapElements[i].snapping = false; + continue; + } + + if(o.snapMode != 'inner') { + var ts = Math.abs(t - y2) <= d; + var bs = Math.abs(b - y1) <= d; + var ls = Math.abs(l - x2) <= d; + var rs = Math.abs(r - x1) <= d; + if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; + if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; + if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; + if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; + } + + var first = (ts || bs || ls || rs); + + if(o.snapMode != 'outer') { + var ts = Math.abs(t - y1) <= d; + var bs = Math.abs(b - y2) <= d; + var ls = Math.abs(l - x1) <= d; + var rs = Math.abs(r - x2) <= d; + if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; + if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; + if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; + if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; + } + + if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) + (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + inst.snapElements[i].snapping = (ts || bs || ls || rs || first); + + }; + + } +}); + +$.ui.plugin.add("draggable", "stack", { + start: function(event, ui) { + + var o = $(this).data("draggable").options; + + var group = $.makeArray($(o.stack)).sort(function(a,b) { + return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0); + }); + if (!group.length) { return; } + + var min = parseInt(group[0].style.zIndex) || 0; + $(group).each(function(i) { + this.style.zIndex = min + i; + }); + + this[0].style.zIndex = min + group.length; + + } +}); + +$.ui.plugin.add("draggable", "zIndex", { + start: function(event, ui) { + var t = $(ui.helper), o = $(this).data("draggable").options; + if(t.css("zIndex")) o._zIndex = t.css("zIndex"); + t.css('zIndex', o.zIndex); + }, + stop: function(event, ui) { + var o = $(this).data("draggable").options; + if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex); + } +}); + +})(jQuery); +/* + * jQuery UI Droppable 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Droppables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.mouse.js + * jquery.ui.draggable.js + */ +(function( $, undefined ) { + +$.widget("ui.droppable", { + widgetEventPrefix: "drop", + options: { + accept: '*', + activeClass: false, + addClasses: true, + greedy: false, + hoverClass: false, + scope: 'default', + tolerance: 'intersect' + }, + _create: function() { + + var o = this.options, accept = o.accept; + this.isover = 0; this.isout = 1; + + this.accept = $.isFunction(accept) ? accept : function(d) { + return d.is(accept); + }; + + //Store the droppable's proportions + this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; + + // Add the reference and positions to the manager + $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; + $.ui.ddmanager.droppables[o.scope].push(this); + + (o.addClasses && this.element.addClass("ui-droppable")); + + }, + + destroy: function() { + var drop = $.ui.ddmanager.droppables[this.options.scope]; + for ( var i = 0; i < drop.length; i++ ) + if ( drop[i] == this ) + drop.splice(i, 1); + + this.element + .removeClass("ui-droppable ui-droppable-disabled") + .removeData("droppable") + .unbind(".droppable"); + + return this; + }, + + _setOption: function(key, value) { + + if(key == 'accept') { + this.accept = $.isFunction(value) ? value : function(d) { + return d.is(value); + }; + } + $.Widget.prototype._setOption.apply(this, arguments); + }, + + _activate: function(event) { + var draggable = $.ui.ddmanager.current; + if(this.options.activeClass) this.element.addClass(this.options.activeClass); + (draggable && this._trigger('activate', event, this.ui(draggable))); + }, + + _deactivate: function(event) { + var draggable = $.ui.ddmanager.current; + if(this.options.activeClass) this.element.removeClass(this.options.activeClass); + (draggable && this._trigger('deactivate', event, this.ui(draggable))); + }, + + _over: function(event) { + + var draggable = $.ui.ddmanager.current; + if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element + + if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + if(this.options.hoverClass) this.element.addClass(this.options.hoverClass); + this._trigger('over', event, this.ui(draggable)); + } + + }, + + _out: function(event) { + + var draggable = $.ui.ddmanager.current; + if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element + + if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass); + this._trigger('out', event, this.ui(draggable)); + } + + }, + + _drop: function(event,custom) { + + var draggable = custom || $.ui.ddmanager.current; + if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element + + var childrenIntersection = false; + this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() { + var inst = $.data(this, 'droppable'); + if( + inst.options.greedy + && !inst.options.disabled + && inst.options.scope == draggable.options.scope + && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) + && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) + ) { childrenIntersection = true; return false; } + }); + if(childrenIntersection) return false; + + if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + if(this.options.activeClass) this.element.removeClass(this.options.activeClass); + if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass); + this._trigger('drop', event, this.ui(draggable)); + return this.element; + } + + return false; + + }, + + ui: function(c) { + return { + draggable: (c.currentItem || c.element), + helper: c.helper, + position: c.position, + offset: c.positionAbs + }; + } + +}); + +$.extend($.ui.droppable, { + version: "1.8.12" +}); + +$.ui.intersect = function(draggable, droppable, toleranceMode) { + + if (!droppable.offset) return false; + + var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, + y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height; + var l = droppable.offset.left, r = l + droppable.proportions.width, + t = droppable.offset.top, b = t + droppable.proportions.height; + + switch (toleranceMode) { + case 'fit': + return (l <= x1 && x2 <= r + && t <= y1 && y2 <= b); + break; + case 'intersect': + return (l < x1 + (draggable.helperProportions.width / 2) // Right Half + && x2 - (draggable.helperProportions.width / 2) < r // Left Half + && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half + && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half + break; + case 'pointer': + var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left), + draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top), + isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width); + return isOver; + break; + case 'touch': + return ( + (y1 >= t && y1 <= b) || // Top edge touching + (y2 >= t && y2 <= b) || // Bottom edge touching + (y1 < t && y2 > b) // Surrounded vertically + ) && ( + (x1 >= l && x1 <= r) || // Left edge touching + (x2 >= l && x2 <= r) || // Right edge touching + (x1 < l && x2 > r) // Surrounded horizontally + ); + break; + default: + return false; + break; + } + +}; + +/* + This manager tracks offsets of draggables and droppables +*/ +$.ui.ddmanager = { + current: null, + droppables: { 'default': [] }, + prepareOffsets: function(t, event) { + + var m = $.ui.ddmanager.droppables[t.options.scope] || []; + var type = event ? event.type : null; // workaround for #2317 + var list = (t.currentItem || t.element).find(":data(droppable)").andSelf(); + + droppablesLoop: for (var i = 0; i < m.length; i++) { + + if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted + for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item + m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue + + if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables + + m[i].offset = m[i].element.offset(); + m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; + + } + + }, + drop: function(draggable, event) { + + var dropped = false; + $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { + + if(!this.options) return; + if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) + dropped = dropped || this._drop.call(this, event); + + if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { + this.isout = 1; this.isover = 0; + this._deactivate.call(this, event); + } + + }); + return dropped; + + }, + drag: function(draggable, event) { + + //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. + if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event); + + //Run through all droppables and check their positions based on specific tolerance options + $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { + + if(this.options.disabled || this.greedyChild || !this.visible) return; + var intersects = $.ui.intersect(draggable, this, this.options.tolerance); + + var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null); + if(!c) return; + + var parentInstance; + if (this.options.greedy) { + var parent = this.element.parents(':data(droppable):eq(0)'); + if (parent.length) { + parentInstance = $.data(parent[0], 'droppable'); + parentInstance.greedyChild = (c == 'isover' ? 1 : 0); + } + } + + // we just moved into a greedy child + if (parentInstance && c == 'isover') { + parentInstance['isover'] = 0; + parentInstance['isout'] = 1; + parentInstance._out.call(parentInstance, event); + } + + this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0; + this[c == "isover" ? "_over" : "_out"].call(this, event); + + // we just moved out of a greedy child + if (parentInstance && c == 'isout') { + parentInstance['isout'] = 0; + parentInstance['isover'] = 1; + parentInstance._over.call(parentInstance, event); + } + }); + + } +}; + +})(jQuery); +/* + * jQuery UI Resizable 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Resizables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget("ui.resizable", $.ui.mouse, { + widgetEventPrefix: "resize", + options: { + alsoResize: false, + animate: false, + animateDuration: "slow", + animateEasing: "swing", + aspectRatio: false, + autoHide: false, + containment: false, + ghost: false, + grid: false, + handles: "e,s,se", + helper: false, + maxHeight: null, + maxWidth: null, + minHeight: 10, + minWidth: 10, + zIndex: 1000 + }, + _create: function() { + + var self = this, o = this.options; + this.element.addClass("ui-resizable"); + + $.extend(this, { + _aspectRatio: !!(o.aspectRatio), + aspectRatio: o.aspectRatio, + originalElement: this.element, + _proportionallyResizeElements: [], + _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null + }); + + //Wrap the element if it cannot hold child nodes + if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { + + //Opera fix for relative positioning + if (/relative/.test(this.element.css('position')) && $.browser.opera) + this.element.css({ position: 'relative', top: 'auto', left: 'auto' }); + + //Create a wrapper element and set the wrapper to the new current internal element + this.element.wrap( + $('
    ').css({ + position: this.element.css('position'), + width: this.element.outerWidth(), + height: this.element.outerHeight(), + top: this.element.css('top'), + left: this.element.css('left') + }) + ); + + //Overwrite the original this.element + this.element = this.element.parent().data( + "resizable", this.element.data('resizable') + ); + + this.elementIsWrapper = true; + + //Move margins to the wrapper + this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); + this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); + + //Prevent Safari textarea resize + this.originalResizeStyle = this.originalElement.css('resize'); + this.originalElement.css('resize', 'none'); + + //Push the actual element to our proportionallyResize internal array + this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' })); + + // avoid IE jump (hard set the margin) + this.originalElement.css({ margin: this.originalElement.css('margin') }); + + // fix handlers offset + this._proportionallyResize(); + + } + + this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' }); + if(this.handles.constructor == String) { + + if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw'; + var n = this.handles.split(","); this.handles = {}; + + for(var i = 0; i < n.length; i++) { + + var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle; + var axis = $('
    '); + + // increase zIndex of sw, se, ne, nw axis + //TODO : this modifies original option + if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex }); + + //TODO : What's going on here? + if ('se' == handle) { + axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se'); + }; + + //Insert into internal handles object and append to element + this.handles[handle] = '.ui-resizable-'+handle; + this.element.append(axis); + } + + } + + this._renderAxis = function(target) { + + target = target || this.element; + + for(var i in this.handles) { + + if(this.handles[i].constructor == String) + this.handles[i] = $(this.handles[i], this.element).show(); + + //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) + if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { + + var axis = $(this.handles[i], this.element), padWrapper = 0; + + //Checking the correct pad and border + padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); + + //The padding type i have to apply... + var padPos = [ 'padding', + /ne|nw|n/.test(i) ? 'Top' : + /se|sw|s/.test(i) ? 'Bottom' : + /^e$/.test(i) ? 'Right' : 'Left' ].join(""); + + target.css(padPos, padWrapper); + + this._proportionallyResize(); + + } + + //TODO: What's that good for? There's not anything to be executed left + if(!$(this.handles[i]).length) + continue; + + } + }; + + //TODO: make renderAxis a prototype function + this._renderAxis(this.element); + + this._handles = $('.ui-resizable-handle', this.element) + .disableSelection(); + + //Matching axis name + this._handles.mouseover(function() { + if (!self.resizing) { + if (this.className) + var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); + //Axis, default = se + self.axis = axis && axis[1] ? axis[1] : 'se'; + } + }); + + //If we want to auto hide the elements + if (o.autoHide) { + this._handles.hide(); + $(this.element) + .addClass("ui-resizable-autohide") + .hover(function() { + $(this).removeClass("ui-resizable-autohide"); + self._handles.show(); + }, + function(){ + if (!self.resizing) { + $(this).addClass("ui-resizable-autohide"); + self._handles.hide(); + } + }); + } + + //Initialize the mouse interaction + this._mouseInit(); + + }, + + destroy: function() { + + this._mouseDestroy(); + + var _destroy = function(exp) { + $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") + .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove(); + }; + + //TODO: Unwrap at same DOM position + if (this.elementIsWrapper) { + _destroy(this.element); + var wrapper = this.element; + wrapper.after( + this.originalElement.css({ + position: wrapper.css('position'), + width: wrapper.outerWidth(), + height: wrapper.outerHeight(), + top: wrapper.css('top'), + left: wrapper.css('left') + }) + ).remove(); + } + + this.originalElement.css('resize', this.originalResizeStyle); + _destroy(this.originalElement); + + return this; + }, + + _mouseCapture: function(event) { + var handle = false; + for (var i in this.handles) { + if ($(this.handles[i])[0] == event.target) { + handle = true; + } + } + + return !this.options.disabled && handle; + }, + + _mouseStart: function(event) { + + var o = this.options, iniPos = this.element.position(), el = this.element; + + this.resizing = true; + this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() }; + + // bugfix for http://dev.jquery.com/ticket/1749 + if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) { + el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left }); + } + + //Opera fixing relative position + if ($.browser.opera && (/relative/).test(el.css('position'))) + el.css({ position: 'relative', top: 'auto', left: 'auto' }); + + this._renderProxy(); + + var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top')); + + if (o.containment) { + curleft += $(o.containment).scrollLeft() || 0; + curtop += $(o.containment).scrollTop() || 0; + } + + //Store needed variables + this.offset = this.helper.offset(); + this.position = { left: curleft, top: curtop }; + this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; + this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; + this.originalPosition = { left: curleft, top: curtop }; + this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; + this.originalMousePosition = { left: event.pageX, top: event.pageY }; + + //Aspect Ratio + this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); + + var cursor = $('.ui-resizable-' + this.axis).css('cursor'); + $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor); + + el.addClass("ui-resizable-resizing"); + this._propagate("start", event); + return true; + }, + + _mouseDrag: function(event) { + + //Increase performance, avoid regex + var el = this.helper, o = this.options, props = {}, + self = this, smp = this.originalMousePosition, a = this.axis; + + var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0; + var trigger = this._change[a]; + if (!trigger) return false; + + // Calculate the attrs that will be change + var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff; + + if (this._aspectRatio || event.shiftKey) + data = this._updateRatio(data, event); + + data = this._respectSize(data, event); + + // plugins callbacks need to be called first + this._propagate("resize", event); + + el.css({ + top: this.position.top + "px", left: this.position.left + "px", + width: this.size.width + "px", height: this.size.height + "px" + }); + + if (!this._helper && this._proportionallyResizeElements.length) + this._proportionallyResize(); + + this._updateCache(data); + + // calling the user callback at the end + this._trigger('resize', event, this.ui()); + + return false; + }, + + _mouseStop: function(event) { + + this.resizing = false; + var o = this.options, self = this; + + if(this._helper) { + var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName), + soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, + soffsetw = ista ? 0 : self.sizeDiff.width; + + var s = { width: (self.helper.width() - soffsetw), height: (self.helper.height() - soffseth) }, + left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, + top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; + + if (!o.animate) + this.element.css($.extend(s, { top: top, left: left })); + + self.helper.height(self.size.height); + self.helper.width(self.size.width); + + if (this._helper && !o.animate) this._proportionallyResize(); + } + + $('body').css('cursor', 'auto'); + + this.element.removeClass("ui-resizable-resizing"); + + this._propagate("stop", event); + + if (this._helper) this.helper.remove(); + return false; + + }, + + _updateCache: function(data) { + var o = this.options; + this.offset = this.helper.offset(); + if (isNumber(data.left)) this.position.left = data.left; + if (isNumber(data.top)) this.position.top = data.top; + if (isNumber(data.height)) this.size.height = data.height; + if (isNumber(data.width)) this.size.width = data.width; + }, + + _updateRatio: function(data, event) { + + var o = this.options, cpos = this.position, csize = this.size, a = this.axis; + + if (data.height) data.width = (csize.height * this.aspectRatio); + else if (data.width) data.height = (csize.width / this.aspectRatio); + + if (a == 'sw') { + data.left = cpos.left + (csize.width - data.width); + data.top = null; + } + if (a == 'nw') { + data.top = cpos.top + (csize.height - data.height); + data.left = cpos.left + (csize.width - data.width); + } + + return data; + }, + + _respectSize: function(data, event) { + + var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis, + ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), + isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height); + + if (isminw) data.width = o.minWidth; + if (isminh) data.height = o.minHeight; + if (ismaxw) data.width = o.maxWidth; + if (ismaxh) data.height = o.maxHeight; + + var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height; + var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); + + if (isminw && cw) data.left = dw - o.minWidth; + if (ismaxw && cw) data.left = dw - o.maxWidth; + if (isminh && ch) data.top = dh - o.minHeight; + if (ismaxh && ch) data.top = dh - o.maxHeight; + + // fixing jump error on top/left - bug #2330 + var isNotwh = !data.width && !data.height; + if (isNotwh && !data.left && data.top) data.top = null; + else if (isNotwh && !data.top && data.left) data.left = null; + + return data; + }, + + _proportionallyResize: function() { + + var o = this.options; + if (!this._proportionallyResizeElements.length) return; + var element = this.helper || this.element; + + for (var i=0; i < this._proportionallyResizeElements.length; i++) { + + var prel = this._proportionallyResizeElements[i]; + + if (!this.borderDif) { + var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')], + p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')]; + + this.borderDif = $.map(b, function(v, i) { + var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0; + return border + padding; + }); + } + + if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length))) + continue; + + prel.css({ + height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, + width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 + }); + + }; + + }, + + _renderProxy: function() { + + var el = this.element, o = this.options; + this.elementOffset = el.offset(); + + if(this._helper) { + + this.helper = this.helper || $('
    '); + + // fix ie6 offset TODO: This seems broken + var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0), + pxyoffset = ( ie6 ? 2 : -1 ); + + this.helper.addClass(this._helper).css({ + width: this.element.outerWidth() + pxyoffset, + height: this.element.outerHeight() + pxyoffset, + position: 'absolute', + left: this.elementOffset.left - ie6offset +'px', + top: this.elementOffset.top - ie6offset +'px', + zIndex: ++o.zIndex //TODO: Don't modify option + }); + + this.helper + .appendTo("body") + .disableSelection(); + + } else { + this.helper = this.element; + } + + }, + + _change: { + e: function(event, dx, dy) { + return { width: this.originalSize.width + dx }; + }, + w: function(event, dx, dy) { + var o = this.options, cs = this.originalSize, sp = this.originalPosition; + return { left: sp.left + dx, width: cs.width - dx }; + }, + n: function(event, dx, dy) { + var o = this.options, cs = this.originalSize, sp = this.originalPosition; + return { top: sp.top + dy, height: cs.height - dy }; + }, + s: function(event, dx, dy) { + return { height: this.originalSize.height + dy }; + }, + se: function(event, dx, dy) { + return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); + }, + sw: function(event, dx, dy) { + return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); + }, + ne: function(event, dx, dy) { + return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); + }, + nw: function(event, dx, dy) { + return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); + } + }, + + _propagate: function(n, event) { + $.ui.plugin.call(this, n, [event, this.ui()]); + (n != "resize" && this._trigger(n, event, this.ui())); + }, + + plugins: {}, + + ui: function() { + return { + originalElement: this.originalElement, + element: this.element, + helper: this.helper, + position: this.position, + size: this.size, + originalSize: this.originalSize, + originalPosition: this.originalPosition + }; + } + +}); + +$.extend($.ui.resizable, { + version: "1.8.12" +}); + +/* + * Resizable Extensions + */ + +$.ui.plugin.add("resizable", "alsoResize", { + + start: function (event, ui) { + var self = $(this).data("resizable"), o = self.options; + + var _store = function (exp) { + $(exp).each(function() { + var el = $(this); + el.data("resizable-alsoresize", { + width: parseInt(el.width(), 10), height: parseInt(el.height(), 10), + left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10), + position: el.css('position') // to reset Opera on stop() + }); + }); + }; + + if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) { + if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } + else { $.each(o.alsoResize, function (exp) { _store(exp); }); } + }else{ + _store(o.alsoResize); + } + }, + + resize: function (event, ui) { + var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition; + + var delta = { + height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0, + top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0 + }, + + _alsoResize = function (exp, c) { + $(exp).each(function() { + var el = $(this), start = $(this).data("resizable-alsoresize"), style = {}, + css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left']; + + $.each(css, function (i, prop) { + var sum = (start[prop]||0) + (delta[prop]||0); + if (sum && sum >= 0) + style[prop] = sum || null; + }); + + // Opera fixing relative position + if ($.browser.opera && /relative/.test(el.css('position'))) { + self._revertToRelativePosition = true; + el.css({ position: 'absolute', top: 'auto', left: 'auto' }); + } + + el.css(style); + }); + }; + + if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) { + $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); }); + }else{ + _alsoResize(o.alsoResize); + } + }, + + stop: function (event, ui) { + var self = $(this).data("resizable"), o = self.options; + + var _reset = function (exp) { + $(exp).each(function() { + var el = $(this); + // reset position for Opera - no need to verify it was changed + el.css({ position: el.data("resizable-alsoresize").position }); + }); + }; + + if (self._revertToRelativePosition) { + self._revertToRelativePosition = false; + if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) { + $.each(o.alsoResize, function (exp) { _reset(exp); }); + }else{ + _reset(o.alsoResize); + } + } + + $(this).removeData("resizable-alsoresize"); + } +}); + +$.ui.plugin.add("resizable", "animate", { + + stop: function(event, ui) { + var self = $(this).data("resizable"), o = self.options; + + var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName), + soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height, + soffsetw = ista ? 0 : self.sizeDiff.width; + + var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) }, + left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null, + top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null; + + self.element.animate( + $.extend(style, top && left ? { top: top, left: left } : {}), { + duration: o.animateDuration, + easing: o.animateEasing, + step: function() { + + var data = { + width: parseInt(self.element.css('width'), 10), + height: parseInt(self.element.css('height'), 10), + top: parseInt(self.element.css('top'), 10), + left: parseInt(self.element.css('left'), 10) + }; + + if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height }); + + // propagating resize, and updating values for each animation step + self._updateCache(data); + self._propagate("resize", event); + + } + } + ); + } + +}); + +$.ui.plugin.add("resizable", "containment", { + + start: function(event, ui) { + var self = $(this).data("resizable"), o = self.options, el = self.element; + var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; + if (!ce) return; + + self.containerElement = $(ce); + + if (/document/.test(oc) || oc == document) { + self.containerOffset = { left: 0, top: 0 }; + self.containerPosition = { left: 0, top: 0 }; + + self.parentData = { + element: $(document), left: 0, top: 0, + width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight + }; + } + + // i'm a node, so compute top, left, right, bottom + else { + var element = $(ce), p = []; + $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); + + self.containerOffset = element.offset(); + self.containerPosition = element.position(); + self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; + + var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width, + width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); + + self.parentData = { + element: ce, left: co.left, top: co.top, width: width, height: height + }; + } + }, + + resize: function(event, ui) { + var self = $(this).data("resizable"), o = self.options, + ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position, + pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement; + + if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co; + + if (cp.left < (self._helper ? co.left : 0)) { + self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left)); + if (pRatio) self.size.height = self.size.width / o.aspectRatio; + self.position.left = o.helper ? co.left : 0; + } + + if (cp.top < (self._helper ? co.top : 0)) { + self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top); + if (pRatio) self.size.width = self.size.height * o.aspectRatio; + self.position.top = self._helper ? co.top : 0; + } + + self.offset.left = self.parentData.left+self.position.left; + self.offset.top = self.parentData.top+self.position.top; + + var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ), + hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height ); + + var isParent = self.containerElement.get(0) == self.element.parent().get(0), + isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position')); + + if(isParent && isOffsetRelative) woset -= self.parentData.left; + + if (woset + self.size.width >= self.parentData.width) { + self.size.width = self.parentData.width - woset; + if (pRatio) self.size.height = self.size.width / self.aspectRatio; + } + + if (hoset + self.size.height >= self.parentData.height) { + self.size.height = self.parentData.height - hoset; + if (pRatio) self.size.width = self.size.height * self.aspectRatio; + } + }, + + stop: function(event, ui){ + var self = $(this).data("resizable"), o = self.options, cp = self.position, + co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement; + + var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height; + + if (self._helper && !o.animate && (/relative/).test(ce.css('position'))) + $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); + + if (self._helper && !o.animate && (/static/).test(ce.css('position'))) + $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); + + } +}); + +$.ui.plugin.add("resizable", "ghost", { + + start: function(event, ui) { + + var self = $(this).data("resizable"), o = self.options, cs = self.size; + + self.ghost = self.originalElement.clone(); + self.ghost + .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) + .addClass('ui-resizable-ghost') + .addClass(typeof o.ghost == 'string' ? o.ghost : ''); + + self.ghost.appendTo(self.helper); + + }, + + resize: function(event, ui){ + var self = $(this).data("resizable"), o = self.options; + if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width }); + }, + + stop: function(event, ui){ + var self = $(this).data("resizable"), o = self.options; + if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0)); + } + +}); + +$.ui.plugin.add("resizable", "grid", { + + resize: function(event, ui) { + var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey; + o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid; + var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1); + + if (/^(se|s|e)$/.test(a)) { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + } + else if (/^(ne)$/.test(a)) { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + self.position.top = op.top - oy; + } + else if (/^(sw)$/.test(a)) { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + self.position.left = op.left - ox; + } + else { + self.size.width = os.width + ox; + self.size.height = os.height + oy; + self.position.top = op.top - oy; + self.position.left = op.left - ox; + } + } + +}); + +var num = function(v) { + return parseInt(v, 10) || 0; +}; + +var isNumber = function(value) { + return !isNaN(parseInt(value, 10)); +}; + +})(jQuery); +/* + * jQuery UI Selectable 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Selectables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget("ui.selectable", $.ui.mouse, { + options: { + appendTo: 'body', + autoRefresh: true, + distance: 0, + filter: '*', + tolerance: 'touch' + }, + _create: function() { + var self = this; + + this.element.addClass("ui-selectable"); + + this.dragged = false; + + // cache selectee children based on filter + var selectees; + this.refresh = function() { + selectees = $(self.options.filter, self.element[0]); + selectees.each(function() { + var $this = $(this); + var pos = $this.offset(); + $.data(this, "selectable-item", { + element: this, + $element: $this, + left: pos.left, + top: pos.top, + right: pos.left + $this.outerWidth(), + bottom: pos.top + $this.outerHeight(), + startselected: false, + selected: $this.hasClass('ui-selected'), + selecting: $this.hasClass('ui-selecting'), + unselecting: $this.hasClass('ui-unselecting') + }); + }); + }; + this.refresh(); + + this.selectees = selectees.addClass("ui-selectee"); + + this._mouseInit(); + + this.helper = $("
    "); + }, + + destroy: function() { + this.selectees + .removeClass("ui-selectee") + .removeData("selectable-item"); + this.element + .removeClass("ui-selectable ui-selectable-disabled") + .removeData("selectable") + .unbind(".selectable"); + this._mouseDestroy(); + + return this; + }, + + _mouseStart: function(event) { + var self = this; + + this.opos = [event.pageX, event.pageY]; + + if (this.options.disabled) + return; + + var options = this.options; + + this.selectees = $(options.filter, this.element[0]); + + this._trigger("start", event); + + $(options.appendTo).append(this.helper); + // position helper (lasso) + this.helper.css({ + "left": event.clientX, + "top": event.clientY, + "width": 0, + "height": 0 + }); + + if (options.autoRefresh) { + this.refresh(); + } + + this.selectees.filter('.ui-selected').each(function() { + var selectee = $.data(this, "selectable-item"); + selectee.startselected = true; + if (!event.metaKey) { + selectee.$element.removeClass('ui-selected'); + selectee.selected = false; + selectee.$element.addClass('ui-unselecting'); + selectee.unselecting = true; + // selectable UNSELECTING callback + self._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + }); + + $(event.target).parents().andSelf().each(function() { + var selectee = $.data(this, "selectable-item"); + if (selectee) { + var doSelect = !event.metaKey || !selectee.$element.hasClass('ui-selected'); + selectee.$element + .removeClass(doSelect ? "ui-unselecting" : "ui-selected") + .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); + selectee.unselecting = !doSelect; + selectee.selecting = doSelect; + selectee.selected = doSelect; + // selectable (UN)SELECTING callback + if (doSelect) { + self._trigger("selecting", event, { + selecting: selectee.element + }); + } else { + self._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + return false; + } + }); + + }, + + _mouseDrag: function(event) { + var self = this; + this.dragged = true; + + if (this.options.disabled) + return; + + var options = this.options; + + var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY; + if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; } + if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; } + this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); + + this.selectees.each(function() { + var selectee = $.data(this, "selectable-item"); + //prevent helper from being selected if appendTo: selectable + if (!selectee || selectee.element == self.element[0]) + return; + var hit = false; + if (options.tolerance == 'touch') { + hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); + } else if (options.tolerance == 'fit') { + hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); + } + + if (hit) { + // SELECT + if (selectee.selected) { + selectee.$element.removeClass('ui-selected'); + selectee.selected = false; + } + if (selectee.unselecting) { + selectee.$element.removeClass('ui-unselecting'); + selectee.unselecting = false; + } + if (!selectee.selecting) { + selectee.$element.addClass('ui-selecting'); + selectee.selecting = true; + // selectable SELECTING callback + self._trigger("selecting", event, { + selecting: selectee.element + }); + } + } else { + // UNSELECT + if (selectee.selecting) { + if (event.metaKey && selectee.startselected) { + selectee.$element.removeClass('ui-selecting'); + selectee.selecting = false; + selectee.$element.addClass('ui-selected'); + selectee.selected = true; + } else { + selectee.$element.removeClass('ui-selecting'); + selectee.selecting = false; + if (selectee.startselected) { + selectee.$element.addClass('ui-unselecting'); + selectee.unselecting = true; + } + // selectable UNSELECTING callback + self._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + } + if (selectee.selected) { + if (!event.metaKey && !selectee.startselected) { + selectee.$element.removeClass('ui-selected'); + selectee.selected = false; + + selectee.$element.addClass('ui-unselecting'); + selectee.unselecting = true; + // selectable UNSELECTING callback + self._trigger("unselecting", event, { + unselecting: selectee.element + }); + } + } + } + }); + + return false; + }, + + _mouseStop: function(event) { + var self = this; + + this.dragged = false; + + var options = this.options; + + $('.ui-unselecting', this.element[0]).each(function() { + var selectee = $.data(this, "selectable-item"); + selectee.$element.removeClass('ui-unselecting'); + selectee.unselecting = false; + selectee.startselected = false; + self._trigger("unselected", event, { + unselected: selectee.element + }); + }); + $('.ui-selecting', this.element[0]).each(function() { + var selectee = $.data(this, "selectable-item"); + selectee.$element.removeClass('ui-selecting').addClass('ui-selected'); + selectee.selecting = false; + selectee.selected = true; + selectee.startselected = true; + self._trigger("selected", event, { + selected: selectee.element + }); + }); + this._trigger("stop", event); + + this.helper.remove(); + + return false; + } + +}); + +$.extend($.ui.selectable, { + version: "1.8.12" +}); + +})(jQuery); +/* + * jQuery UI Sortable 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Sortables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget("ui.sortable", $.ui.mouse, { + widgetEventPrefix: "sort", + options: { + appendTo: "parent", + axis: false, + connectWith: false, + containment: false, + cursor: 'auto', + cursorAt: false, + dropOnEmpty: true, + forcePlaceholderSize: false, + forceHelperSize: false, + grid: false, + handle: false, + helper: "original", + items: '> *', + opacity: false, + placeholder: false, + revert: false, + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + scope: "default", + tolerance: "intersect", + zIndex: 1000 + }, + _create: function() { + + var o = this.options; + this.containerCache = {}; + this.element.addClass("ui-sortable"); + + //Get the items + this.refresh(); + + //Let's determine if the items are being displayed horizontally + this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false; + + //Let's determine the parent's offset + this.offset = this.element.offset(); + + //Initialize mouse events for interaction + this._mouseInit(); + + }, + + destroy: function() { + this.element + .removeClass("ui-sortable ui-sortable-disabled") + .removeData("sortable") + .unbind(".sortable"); + this._mouseDestroy(); + + for ( var i = this.items.length - 1; i >= 0; i-- ) + this.items[i].item.removeData("sortable-item"); + + return this; + }, + + _setOption: function(key, value){ + if ( key === "disabled" ) { + this.options[ key ] = value; + + this.widget() + [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" ); + } else { + // Don't call widget base _setOption for disable as it adds ui-state-disabled class + $.Widget.prototype._setOption.apply(this, arguments); + } + }, + + _mouseCapture: function(event, overrideHandle) { + + if (this.reverting) { + return false; + } + + if(this.options.disabled || this.options.type == 'static') return false; + + //We have to refresh the items data once first + this._refreshItems(event); + + //Find out if the clicked node (or one of its parents) is a actual item in this.items + var currentItem = null, self = this, nodes = $(event.target).parents().each(function() { + if($.data(this, 'sortable-item') == self) { + currentItem = $(this); + return false; + } + }); + if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target); + + if(!currentItem) return false; + if(this.options.handle && !overrideHandle) { + var validHandle = false; + + $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; }); + if(!validHandle) return false; + } + + this.currentItem = currentItem; + this._removeCurrentsFromItems(); + return true; + + }, + + _mouseStart: function(event, overrideHandle, noActivation) { + + var o = this.options, self = this; + this.currentContainer = this; + + //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture + this.refreshPositions(); + + //Create and append the visible helper + this.helper = this._createHelper(event); + + //Cache the helper size + this._cacheHelperProportions(); + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Get the next scrolling parent + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.currentItem.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + // Only after we got the offset, we can change the helper's position to absolute + // TODO: Still need to figure out a way to make relative sorting possible + this.helper.css("position", "absolute"); + this.cssPosition = this.helper.css("position"); + + $.extend(this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper + }); + + //Generate the original position + this.originalPosition = this._generatePosition(event); + this.originalPageX = event.pageX; + this.originalPageY = event.pageY; + + //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied + (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); + + //Cache the former DOM position + this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; + + //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way + if(this.helper[0] != this.currentItem[0]) { + this.currentItem.hide(); + } + + //Create the placeholder + this._createPlaceholder(); + + //Set a containment if given in the options + if(o.containment) + this._setContainment(); + + if(o.cursor) { // cursor option + if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor"); + $('body').css("cursor", o.cursor); + } + + if(o.opacity) { // opacity option + if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity"); + this.helper.css("opacity", o.opacity); + } + + if(o.zIndex) { // zIndex option + if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex"); + this.helper.css("zIndex", o.zIndex); + } + + //Prepare scrolling + if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') + this.overflowOffset = this.scrollParent.offset(); + + //Call callbacks + this._trigger("start", event, this._uiHash()); + + //Recache the helper size + if(!this._preserveHelperProportions) + this._cacheHelperProportions(); + + + //Post 'activate' events to possible containers + if(!noActivation) { + for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); } + } + + //Prepare possible droppables + if($.ui.ddmanager) + $.ui.ddmanager.current = this; + + if ($.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(this, event); + + this.dragging = true; + + this.helper.addClass("ui-sortable-helper"); + this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position + return true; + + }, + + _mouseDrag: function(event) { + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + if (!this.lastPositionAbs) { + this.lastPositionAbs = this.positionAbs; + } + + //Do scrolling + if(this.options.scroll) { + var o = this.options, scrolled = false; + if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') { + + if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) + this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; + else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) + this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; + + if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) + this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; + else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) + this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; + + } else { + + if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); + else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); + + if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); + else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); + + } + + if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(this, event); + } + + //Regenerate the absolute position used for position checks + this.positionAbs = this._convertPositionTo("absolute"); + + //Set the helper position + if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px'; + if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px'; + + //Rearrange + for (var i = this.items.length - 1; i >= 0; i--) { + + //Cache variables and intersection, continue if no intersection + var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item); + if (!intersection) continue; + + if(itemElement != this.currentItem[0] //cannot intersect with itself + && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before + && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked + && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true) + //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container + ) { + + this.direction = intersection == 1 ? "down" : "up"; + + if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) { + this._rearrange(event, item); + } else { + break; + } + + this._trigger("change", event, this._uiHash()); + break; + } + } + + //Post events to containers + this._contactContainers(event); + + //Interconnect with droppables + if($.ui.ddmanager) $.ui.ddmanager.drag(this, event); + + //Call callbacks + this._trigger('sort', event, this._uiHash()); + + this.lastPositionAbs = this.positionAbs; + return false; + + }, + + _mouseStop: function(event, noPropagation) { + + if(!event) return; + + //If we are using droppables, inform the manager about the drop + if ($.ui.ddmanager && !this.options.dropBehaviour) + $.ui.ddmanager.drop(this, event); + + if(this.options.revert) { + var self = this; + var cur = self.placeholder.offset(); + + self.reverting = true; + + $(this.helper).animate({ + left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft), + top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop) + }, parseInt(this.options.revert, 10) || 500, function() { + self._clear(event); + }); + } else { + this._clear(event, noPropagation); + } + + return false; + + }, + + cancel: function() { + + var self = this; + + if(this.dragging) { + + this._mouseUp({ target: null }); + + if(this.options.helper == "original") + this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); + else + this.currentItem.show(); + + //Post deactivating events to containers + for (var i = this.containers.length - 1; i >= 0; i--){ + this.containers[i]._trigger("deactivate", null, self._uiHash(this)); + if(this.containers[i].containerCache.over) { + this.containers[i]._trigger("out", null, self._uiHash(this)); + this.containers[i].containerCache.over = 0; + } + } + + } + + if (this.placeholder) { + //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! + if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]); + if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove(); + + $.extend(this, { + helper: null, + dragging: false, + reverting: false, + _noFinalSort: null + }); + + if(this.domPosition.prev) { + $(this.domPosition.prev).after(this.currentItem); + } else { + $(this.domPosition.parent).prepend(this.currentItem); + } + } + + return this; + + }, + + serialize: function(o) { + + var items = this._getItemsAsjQuery(o && o.connected); + var str = []; o = o || {}; + + $(items).each(function() { + var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/)); + if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2])); + }); + + if(!str.length && o.key) { + str.push(o.key + '='); + } + + return str.join('&'); + + }, + + toArray: function(o) { + + var items = this._getItemsAsjQuery(o && o.connected); + var ret = []; o = o || {}; + + items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); }); + return ret; + + }, + + /* Be careful with the following core functions */ + _intersectsWith: function(item) { + + var x1 = this.positionAbs.left, + x2 = x1 + this.helperProportions.width, + y1 = this.positionAbs.top, + y2 = y1 + this.helperProportions.height; + + var l = item.left, + r = l + item.width, + t = item.top, + b = t + item.height; + + var dyClick = this.offset.click.top, + dxClick = this.offset.click.left; + + var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r; + + if( this.options.tolerance == "pointer" + || this.options.forcePointerForContainers + || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height']) + ) { + return isOverElement; + } else { + + return (l < x1 + (this.helperProportions.width / 2) // Right Half + && x2 - (this.helperProportions.width / 2) < r // Left Half + && t < y1 + (this.helperProportions.height / 2) // Bottom Half + && y2 - (this.helperProportions.height / 2) < b ); // Top Half + + } + }, + + _intersectsWithPointer: function(item) { + + var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), + isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), + isOverElement = isOverElementHeight && isOverElementWidth, + verticalDirection = this._getDragVerticalDirection(), + horizontalDirection = this._getDragHorizontalDirection(); + + if (!isOverElement) + return false; + + return this.floating ? + ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 ) + : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) ); + + }, + + _intersectsWithSides: function(item) { + + var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), + isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), + verticalDirection = this._getDragVerticalDirection(), + horizontalDirection = this._getDragHorizontalDirection(); + + if (this.floating && horizontalDirection) { + return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf)); + } else { + return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf)); + } + + }, + + _getDragVerticalDirection: function() { + var delta = this.positionAbs.top - this.lastPositionAbs.top; + return delta != 0 && (delta > 0 ? "down" : "up"); + }, + + _getDragHorizontalDirection: function() { + var delta = this.positionAbs.left - this.lastPositionAbs.left; + return delta != 0 && (delta > 0 ? "right" : "left"); + }, + + refresh: function(event) { + this._refreshItems(event); + this.refreshPositions(); + return this; + }, + + _connectWith: function() { + var options = this.options; + return options.connectWith.constructor == String + ? [options.connectWith] + : options.connectWith; + }, + + _getItemsAsjQuery: function(connected) { + + var self = this; + var items = []; + var queries = []; + var connectWith = this._connectWith(); + + if(connectWith && connected) { + for (var i = connectWith.length - 1; i >= 0; i--){ + var cur = $(connectWith[i]); + for (var j = cur.length - 1; j >= 0; j--){ + var inst = $.data(cur[j], 'sortable'); + if(inst && inst != this && !inst.options.disabled) { + queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]); + } + }; + }; + } + + queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]); + + for (var i = queries.length - 1; i >= 0; i--){ + queries[i][0].each(function() { + items.push(this); + }); + }; + + return $(items); + + }, + + _removeCurrentsFromItems: function() { + + var list = this.currentItem.find(":data(sortable-item)"); + + for (var i=0; i < this.items.length; i++) { + + for (var j=0; j < list.length; j++) { + if(list[j] == this.items[i].item[0]) + this.items.splice(i,1); + }; + + }; + + }, + + _refreshItems: function(event) { + + this.items = []; + this.containers = [this]; + var items = this.items; + var self = this; + var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]]; + var connectWith = this._connectWith(); + + if(connectWith) { + for (var i = connectWith.length - 1; i >= 0; i--){ + var cur = $(connectWith[i]); + for (var j = cur.length - 1; j >= 0; j--){ + var inst = $.data(cur[j], 'sortable'); + if(inst && inst != this && !inst.options.disabled) { + queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); + this.containers.push(inst); + } + }; + }; + } + + for (var i = queries.length - 1; i >= 0; i--) { + var targetData = queries[i][1]; + var _queries = queries[i][0]; + + for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) { + var item = $(_queries[j]); + + item.data('sortable-item', targetData); // Data for target checking (mouse manager) + + items.push({ + item: item, + instance: targetData, + width: 0, height: 0, + left: 0, top: 0 + }); + }; + }; + + }, + + refreshPositions: function(fast) { + + //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change + if(this.offsetParent && this.helper) { + this.offset.parent = this._getParentOffset(); + } + + for (var i = this.items.length - 1; i >= 0; i--){ + var item = this.items[i]; + + //We ignore calculating positions of all connected containers when we're not over them + if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0]) + continue; + + var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; + + if (!fast) { + item.width = t.outerWidth(); + item.height = t.outerHeight(); + } + + var p = t.offset(); + item.left = p.left; + item.top = p.top; + }; + + if(this.options.custom && this.options.custom.refreshContainers) { + this.options.custom.refreshContainers.call(this); + } else { + for (var i = this.containers.length - 1; i >= 0; i--){ + var p = this.containers[i].element.offset(); + this.containers[i].containerCache.left = p.left; + this.containers[i].containerCache.top = p.top; + this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); + this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); + }; + } + + return this; + }, + + _createPlaceholder: function(that) { + + var self = that || this, o = self.options; + + if(!o.placeholder || o.placeholder.constructor == String) { + var className = o.placeholder; + o.placeholder = { + element: function() { + + var el = $(document.createElement(self.currentItem[0].nodeName)) + .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder") + .removeClass("ui-sortable-helper")[0]; + + if(!className) + el.style.visibility = "hidden"; + + return el; + }, + update: function(container, p) { + + // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that + // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified + if(className && !o.forcePlaceholderSize) return; + + //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item + if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); }; + if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); }; + } + }; + } + + //Create the placeholder + self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem)); + + //Append it after the actual current item + self.currentItem.after(self.placeholder); + + //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) + o.placeholder.update(self, self.placeholder); + + }, + + _contactContainers: function(event) { + + // get innermost container that intersects with item + var innermostContainer = null, innermostIndex = null; + + + for (var i = this.containers.length - 1; i >= 0; i--){ + + // never consider a container that's located within the item itself + if($.ui.contains(this.currentItem[0], this.containers[i].element[0])) + continue; + + if(this._intersectsWith(this.containers[i].containerCache)) { + + // if we've already found a container and it's more "inner" than this, then continue + if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0])) + continue; + + innermostContainer = this.containers[i]; + innermostIndex = i; + + } else { + // container doesn't intersect. trigger "out" event if necessary + if(this.containers[i].containerCache.over) { + this.containers[i]._trigger("out", event, this._uiHash(this)); + this.containers[i].containerCache.over = 0; + } + } + + } + + // if no intersecting containers found, return + if(!innermostContainer) return; + + // move the item into the container if it's not there already + if(this.containers.length === 1) { + this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); + this.containers[innermostIndex].containerCache.over = 1; + } else if(this.currentContainer != this.containers[innermostIndex]) { + + //When entering a new container, we will find the item with the least distance and append our item near it + var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top']; + for (var j = this.items.length - 1; j >= 0; j--) { + if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue; + var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top']; + if(Math.abs(cur - base) < dist) { + dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; + } + } + + if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled + return; + + this.currentContainer = this.containers[innermostIndex]; + itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); + this._trigger("change", event, this._uiHash()); + this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); + + //Update the placeholder + this.options.placeholder.update(this.currentContainer, this.placeholder); + + this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); + this.containers[innermostIndex].containerCache.over = 1; + } + + + }, + + _createHelper: function(event) { + + var o = this.options; + var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem); + + if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already + $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); + + if(helper[0] == this.currentItem[0]) + this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; + + if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width()); + if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height()); + + return helper; + + }, + + _adjustOffsetFromHelper: function(obj) { + if (typeof obj == 'string') { + obj = obj.split(' '); + } + if ($.isArray(obj)) { + obj = {left: +obj[0], top: +obj[1] || 0}; + } + if ('left' in obj) { + this.offset.click.left = obj.left + this.margins.left; + } + if ('right' in obj) { + this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + } + if ('top' in obj) { + this.offset.click.top = obj.top + this.margins.top; + } + if ('bottom' in obj) { + this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + } + }, + + _getParentOffset: function() { + + + //Get the offsetParent and cache its position + this.offsetParent = this.helper.offsetParent(); + var po = this.offsetParent.offset(); + + // This is a special case where we need to modify a offset calculated on start, since the following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that + // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag + if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) { + po.left += this.scrollParent.scrollLeft(); + po.top += this.scrollParent.scrollTop(); + } + + if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information + || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix + po = { top: 0, left: 0 }; + + return { + top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), + left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) + }; + + }, + + _getRelativeOffset: function() { + + if(this.cssPosition == "relative") { + var p = this.currentItem.position(); + return { + top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), + left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), + top: (parseInt(this.currentItem.css("marginTop"),10) || 0) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var o = this.options; + if(o.containment == 'parent') o.containment = this.helper[0].parentNode; + if(o.containment == 'document' || o.containment == 'window') this.containment = [ + 0 - this.offset.relative.left - this.offset.parent.left, + 0 - this.offset.relative.top - this.offset.parent.top, + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left, + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top + ]; + + if(!(/^(document|window|parent)$/).test(o.containment)) { + var ce = $(o.containment)[0]; + var co = $(o.containment).offset(); + var over = ($(ce).css("overflow") != 'hidden'); + + this.containment = [ + co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, + co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, + co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, + co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top + ]; + } + + }, + + _convertPositionTo: function(d, pos) { + + if(!pos) pos = this.position; + var mod = d == "absolute" ? 1 : -1; + var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + return { + top: ( + pos.top // The absolute mouse position + + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border) + - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) + ), + left: ( + pos.left // The absolute mouse position + + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border) + - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) + ) + }; + + }, + + _generatePosition: function(event) { + + var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + // This is another very weird special case that only happens for relative elements: + // 1. If the css position is relative + // 2. and the scroll parent is the document or similar to the offset parent + // we have to refresh the relative offset during the scroll so there are no jumps + if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) { + this.offset.relative = this._getRelativeOffset(); + } + + var pageX = event.pageX; + var pageY = event.pageY; + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + + if(this.originalPosition) { //If we are not dragging yet, we won't check for options + + if(this.containment) { + if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left; + if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top; + if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left; + if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top; + } + + if(o.grid) { + var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; + pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; + + var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; + pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; + } + + } + + return { + top: ( + pageY // The absolute mouse position + - this.offset.click.top // Click offset (relative to the element) + - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.top // The offsetParent's offset without borders (offset + border) + + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) + ), + left: ( + pageX // The absolute mouse position + - this.offset.click.left // Click offset (relative to the element) + - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.left // The offsetParent's offset without borders (offset + border) + + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) + ) + }; + + }, + + _rearrange: function(event, i, a, hardRefresh) { + + a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling)); + + //Various things done here to improve the performance: + // 1. we create a setTimeout, that calls refreshPositions + // 2. on the instance, we have a counter variable, that get's higher after every append + // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same + // 4. this lets only the last addition to the timeout stack through + this.counter = this.counter ? ++this.counter : 1; + var self = this, counter = this.counter; + + window.setTimeout(function() { + if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove + },0); + + }, + + _clear: function(event, noPropagation) { + + this.reverting = false; + // We delay all events that have to be triggered to after the point where the placeholder has been removed and + // everything else normalized again + var delayedTriggers = [], self = this; + + // We first have to update the dom position of the actual currentItem + // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) + if(!this._noFinalSort && this.currentItem[0].parentNode) this.placeholder.before(this.currentItem); + this._noFinalSort = null; + + if(this.helper[0] == this.currentItem[0]) { + for(var i in this._storedCSS) { + if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = ''; + } + this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); + } else { + this.currentItem.show(); + } + + if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); + if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed + if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element + if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); + for (var i = this.containers.length - 1; i >= 0; i--){ + if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) { + delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.containers[i])); + delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.containers[i])); + } + }; + }; + + //Post events to containers + for (var i = this.containers.length - 1; i >= 0; i--){ + if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); + if(this.containers[i].containerCache.over) { + delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); + this.containers[i].containerCache.over = 0; + } + } + + //Do what was originally in plugins + if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor + if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity + if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index + + this.dragging = false; + if(this.cancelHelperRemoval) { + if(!noPropagation) { + this._trigger("beforeStop", event, this._uiHash()); + for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events + this._trigger("stop", event, this._uiHash()); + } + return false; + } + + if(!noPropagation) this._trigger("beforeStop", event, this._uiHash()); + + //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! + this.placeholder[0].parentNode.removeChild(this.placeholder[0]); + + if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null; + + if(!noPropagation) { + for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events + this._trigger("stop", event, this._uiHash()); + } + + this.fromOutside = false; + return true; + + }, + + _trigger: function() { + if ($.Widget.prototype._trigger.apply(this, arguments) === false) { + this.cancel(); + } + }, + + _uiHash: function(inst) { + var self = inst || this; + return { + helper: self.helper, + placeholder: self.placeholder || $([]), + position: self.position, + originalPosition: self.originalPosition, + offset: self.positionAbs, + item: self.currentItem, + sender: inst ? inst.element : null + }; + } + +}); + +$.extend($.ui.sortable, { + version: "1.8.12" +}); + +})(jQuery); +/* + * jQuery UI Effects 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/ + */ +;jQuery.effects || (function($, undefined) { + +$.effects = {}; + + + +/******************************************************************************/ +/****************************** COLOR ANIMATIONS ******************************/ +/******************************************************************************/ + +// override the animation for color styles +$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', + 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'], +function(i, attr) { + $.fx.step[attr] = function(fx) { + if (!fx.colorInit) { + fx.start = getColor(fx.elem, attr); + fx.end = getRGB(fx.end); + fx.colorInit = true; + } + + fx.elem.style[attr] = 'rgb(' + + Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' + + Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' + + Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')'; + }; +}); + +// Color Conversion functions from highlightFade +// By Blair Mitchelmore +// http://jquery.offput.ca/highlightFade/ + +// Parse strings looking for color tuples [255,255,255] +function getRGB(color) { + var result; + + // Check if we're already dealing with an array of colors + if ( color && color.constructor == Array && color.length == 3 ) + return color; + + // Look for rgb(num,num,num) + if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) + return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)]; + + // Look for rgb(num%,num%,num%) + if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) + return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55]; + + // Look for #a0b1c2 + if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) + return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)]; + + // Look for #fff + if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) + return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)]; + + // Look for rgba(0, 0, 0, 0) == transparent in Safari 3 + if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) + return colors['transparent']; + + // Otherwise, we're most likely dealing with a named color + return colors[$.trim(color).toLowerCase()]; +} + +function getColor(elem, attr) { + var color; + + do { + color = $.curCSS(elem, attr); + + // Keep going until we find an element that has color, or we hit the body + if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") ) + break; + + attr = "backgroundColor"; + } while ( elem = elem.parentNode ); + + return getRGB(color); +}; + +// Some named colors to work with +// From Interface by Stefan Petre +// http://interface.eyecon.ro/ + +var colors = { + aqua:[0,255,255], + azure:[240,255,255], + beige:[245,245,220], + black:[0,0,0], + blue:[0,0,255], + brown:[165,42,42], + cyan:[0,255,255], + darkblue:[0,0,139], + darkcyan:[0,139,139], + darkgrey:[169,169,169], + darkgreen:[0,100,0], + darkkhaki:[189,183,107], + darkmagenta:[139,0,139], + darkolivegreen:[85,107,47], + darkorange:[255,140,0], + darkorchid:[153,50,204], + darkred:[139,0,0], + darksalmon:[233,150,122], + darkviolet:[148,0,211], + fuchsia:[255,0,255], + gold:[255,215,0], + green:[0,128,0], + indigo:[75,0,130], + khaki:[240,230,140], + lightblue:[173,216,230], + lightcyan:[224,255,255], + lightgreen:[144,238,144], + lightgrey:[211,211,211], + lightpink:[255,182,193], + lightyellow:[255,255,224], + lime:[0,255,0], + magenta:[255,0,255], + maroon:[128,0,0], + navy:[0,0,128], + olive:[128,128,0], + orange:[255,165,0], + pink:[255,192,203], + purple:[128,0,128], + violet:[128,0,128], + red:[255,0,0], + silver:[192,192,192], + white:[255,255,255], + yellow:[255,255,0], + transparent: [255,255,255] +}; + + + +/******************************************************************************/ +/****************************** CLASS ANIMATIONS ******************************/ +/******************************************************************************/ + +var classAnimationActions = ['add', 'remove', 'toggle'], + shorthandStyles = { + border: 1, + borderBottom: 1, + borderColor: 1, + borderLeft: 1, + borderRight: 1, + borderTop: 1, + borderWidth: 1, + margin: 1, + padding: 1 + }; + +function getElementStyles() { + var style = document.defaultView + ? document.defaultView.getComputedStyle(this, null) + : this.currentStyle, + newStyle = {}, + key, + camelCase; + + // webkit enumerates style porperties + if (style && style.length && style[0] && style[style[0]]) { + var len = style.length; + while (len--) { + key = style[len]; + if (typeof style[key] == 'string') { + camelCase = key.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + newStyle[camelCase] = style[key]; + } + } + } else { + for (key in style) { + if (typeof style[key] === 'string') { + newStyle[key] = style[key]; + } + } + } + + return newStyle; +} + +function filterStyles(styles) { + var name, value; + for (name in styles) { + value = styles[name]; + if ( + // ignore null and undefined values + value == null || + // ignore functions (when does this occur?) + $.isFunction(value) || + // shorthand styles that need to be expanded + name in shorthandStyles || + // ignore scrollbars (break in IE) + (/scrollbar/).test(name) || + + // only colors or values that can be converted to numbers + (!(/color/i).test(name) && isNaN(parseFloat(value))) + ) { + delete styles[name]; + } + } + + return styles; +} + +function styleDifference(oldStyle, newStyle) { + var diff = { _: 0 }, // http://dev.jquery.com/ticket/5459 + name; + + for (name in newStyle) { + if (oldStyle[name] != newStyle[name]) { + diff[name] = newStyle[name]; + } + } + + return diff; +} + +$.effects.animateClass = function(value, duration, easing, callback) { + if ($.isFunction(easing)) { + callback = easing; + easing = null; + } + + return this.queue('fx', function() { + var that = $(this), + originalStyleAttr = that.attr('style') || ' ', + originalStyle = filterStyles(getElementStyles.call(this)), + newStyle, + className = that.attr('className'); + + $.each(classAnimationActions, function(i, action) { + if (value[action]) { + that[action + 'Class'](value[action]); + } + }); + newStyle = filterStyles(getElementStyles.call(this)); + that.attr('className', className); + + that.animate(styleDifference(originalStyle, newStyle), duration, easing, function() { + $.each(classAnimationActions, function(i, action) { + if (value[action]) { that[action + 'Class'](value[action]); } + }); + // work around bug in IE by clearing the cssText before setting it + if (typeof that.attr('style') == 'object') { + that.attr('style').cssText = ''; + that.attr('style').cssText = originalStyleAttr; + } else { + that.attr('style', originalStyleAttr); + } + if (callback) { callback.apply(this, arguments); } + }); + + // $.animate adds a function to the end of the queue + // but we want it at the front + var queue = $.queue(this), + anim = queue.splice(queue.length - 1, 1)[0]; + queue.splice(1, 0, anim); + $.dequeue(this); + }); +}; + +$.fn.extend({ + _addClass: $.fn.addClass, + addClass: function(classNames, speed, easing, callback) { + return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames); + }, + + _removeClass: $.fn.removeClass, + removeClass: function(classNames,speed,easing,callback) { + return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames); + }, + + _toggleClass: $.fn.toggleClass, + toggleClass: function(classNames, force, speed, easing, callback) { + if ( typeof force == "boolean" || force === undefined ) { + if ( !speed ) { + // without speed parameter; + return this._toggleClass(classNames, force); + } else { + return $.effects.animateClass.apply(this, [(force?{add:classNames}:{remove:classNames}),speed,easing,callback]); + } + } else { + // without switch parameter; + return $.effects.animateClass.apply(this, [{ toggle: classNames },force,speed,easing]); + } + }, + + switchClass: function(remove,add,speed,easing,callback) { + return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]); + } +}); + + + +/******************************************************************************/ +/*********************************** EFFECTS **********************************/ +/******************************************************************************/ + +$.extend($.effects, { + version: "1.8.12", + + // Saves a set of properties in a data storage + save: function(element, set) { + for(var i=0; i < set.length; i++) { + if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]); + } + }, + + // Restores a set of previously saved properties from a data storage + restore: function(element, set) { + for(var i=0; i < set.length; i++) { + if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i])); + } + }, + + setMode: function(el, mode) { + if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle + return mode; + }, + + getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value + // this should be a little more flexible in the future to handle a string & hash + var y, x; + switch (origin[0]) { + case 'top': y = 0; break; + case 'middle': y = 0.5; break; + case 'bottom': y = 1; break; + default: y = origin[0] / original.height; + }; + switch (origin[1]) { + case 'left': x = 0; break; + case 'center': x = 0.5; break; + case 'right': x = 1; break; + default: x = origin[1] / original.width; + }; + return {x: x, y: y}; + }, + + // Wraps the element around a wrapper that copies position properties + createWrapper: function(element) { + + // if the element is already wrapped, return it + if (element.parent().is('.ui-effects-wrapper')) { + return element.parent(); + } + + // wrap the element + var props = { + width: element.outerWidth(true), + height: element.outerHeight(true), + 'float': element.css('float') + }, + wrapper = $('
    ') + .addClass('ui-effects-wrapper') + .css({ + fontSize: '100%', + background: 'transparent', + border: 'none', + margin: 0, + padding: 0 + }); + + element.wrap(wrapper); + wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element + + // transfer positioning properties to the wrapper + if (element.css('position') == 'static') { + wrapper.css({ position: 'relative' }); + element.css({ position: 'relative' }); + } else { + $.extend(props, { + position: element.css('position'), + zIndex: element.css('z-index') + }); + $.each(['top', 'left', 'bottom', 'right'], function(i, pos) { + props[pos] = element.css(pos); + if (isNaN(parseInt(props[pos], 10))) { + props[pos] = 'auto'; + } + }); + element.css({position: 'relative', top: 0, left: 0, right: 'auto', bottom: 'auto' }); + } + + return wrapper.css(props).show(); + }, + + removeWrapper: function(element) { + if (element.parent().is('.ui-effects-wrapper')) + return element.parent().replaceWith(element); + return element; + }, + + setTransition: function(element, list, factor, value) { + value = value || {}; + $.each(list, function(i, x){ + unit = element.cssUnit(x); + if (unit[0] > 0) value[x] = unit[0] * factor + unit[1]; + }); + return value; + } +}); + + +function _normalizeArguments(effect, options, speed, callback) { + // shift params for method overloading + if (typeof effect == 'object') { + callback = options; + speed = null; + options = effect; + effect = options.effect; + } + if ($.isFunction(options)) { + callback = options; + speed = null; + options = {}; + } + if (typeof options == 'number' || $.fx.speeds[options]) { + callback = speed; + speed = options; + options = {}; + } + if ($.isFunction(speed)) { + callback = speed; + speed = null; + } + + options = options || {}; + + speed = speed || options.duration; + speed = $.fx.off ? 0 : typeof speed == 'number' + ? speed : speed in $.fx.speeds ? $.fx.speeds[speed] : $.fx.speeds._default; + + callback = callback || options.complete; + + return [effect, options, speed, callback]; +} + +function standardSpeed( speed ) { + // valid standard speeds + if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { + return true; + } + + // invalid strings - treat as "normal" speed + if ( typeof speed === "string" && !$.effects[ speed ] ) { + return true; + } + + return false; +} + +$.fn.extend({ + effect: function(effect, options, speed, callback) { + var args = _normalizeArguments.apply(this, arguments), + // TODO: make effects take actual parameters instead of a hash + args2 = { + options: args[1], + duration: args[2], + callback: args[3] + }, + mode = args2.options.mode, + effectMethod = $.effects[effect]; + + if ( $.fx.off || !effectMethod ) { + // delegate to the original method (e.g., .show()) if possible + if ( mode ) { + return this[ mode ]( args2.duration, args2.callback ); + } else { + return this.each(function() { + if ( args2.callback ) { + args2.callback.call( this ); + } + }); + } + } + + return effectMethod.call(this, args2); + }, + + _show: $.fn.show, + show: function(speed) { + if ( standardSpeed( speed ) ) { + return this._show.apply(this, arguments); + } else { + var args = _normalizeArguments.apply(this, arguments); + args[1].mode = 'show'; + return this.effect.apply(this, args); + } + }, + + _hide: $.fn.hide, + hide: function(speed) { + if ( standardSpeed( speed ) ) { + return this._hide.apply(this, arguments); + } else { + var args = _normalizeArguments.apply(this, arguments); + args[1].mode = 'hide'; + return this.effect.apply(this, args); + } + }, + + // jQuery core overloads toggle and creates _toggle + __toggle: $.fn.toggle, + toggle: function(speed) { + if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) { + return this.__toggle.apply(this, arguments); + } else { + var args = _normalizeArguments.apply(this, arguments); + args[1].mode = 'toggle'; + return this.effect.apply(this, args); + } + }, + + // helper functions + cssUnit: function(key) { + var style = this.css(key), val = []; + $.each( ['em','px','%','pt'], function(i, unit){ + if(style.indexOf(unit) > 0) + val = [parseFloat(style), unit]; + }); + return val; + } +}); + + + +/******************************************************************************/ +/*********************************** EASING ***********************************/ +/******************************************************************************/ + +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built in easing capabilities added In jQuery 1.1 + * to offer multiple easing options + * + * TERMS OF USE - jQuery Easing + * + * Open source under the BSD License. + * + * Copyright 2008 George McGinley Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +// t: current time, b: begInnIng value, c: change In value, d: duration +$.easing.jswing = $.easing.swing; + +$.extend($.easing, +{ + def: 'easeOutQuad', + swing: function (x, t, b, c, d) { + //alert($.easing.default); + return $.easing[$.easing.def](x, t, b, c, d); + }, + easeInQuad: function (x, t, b, c, d) { + return c*(t/=d)*t + b; + }, + easeOutQuad: function (x, t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + easeInOutQuad: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + easeInCubic: function (x, t, b, c, d) { + return c*(t/=d)*t*t + b; + }, + easeOutCubic: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; + }, + easeInOutCubic: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; + }, + easeInQuart: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + easeOutQuart: function (x, t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + easeInOutQuart: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + easeInQuint: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t*t + b; + }, + easeOutQuint: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; + }, + easeInOutQuint: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; + }, + easeInSine: function (x, t, b, c, d) { + return -c * Math.cos(t/d * (Math.PI/2)) + c + b; + }, + easeOutSine: function (x, t, b, c, d) { + return c * Math.sin(t/d * (Math.PI/2)) + b; + }, + easeInOutSine: function (x, t, b, c, d) { + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; + }, + easeInExpo: function (x, t, b, c, d) { + return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; + }, + easeOutExpo: function (x, t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; + }, + easeInOutExpo: function (x, t, b, c, d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; + return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; + }, + easeInCirc: function (x, t, b, c, d) { + return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; + }, + easeOutCirc: function (x, t, b, c, d) { + return c * Math.sqrt(1 - (t=t/d-1)*t) + b; + }, + easeInOutCirc: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; + return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; + }, + easeInElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + easeOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + easeInOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + easeInBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + easeOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + easeInOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + easeInBounce: function (x, t, b, c, d) { + return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b; + }, + easeOutBounce: function (x, t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + easeInOutBounce: function (x, t, b, c, d) { + if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; + return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; + } +}); + +/* + * + * TERMS OF USE - EASING EQUATIONS + * + * Open source under the BSD License. + * + * Copyright 2001 Robert Penner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +})(jQuery); +/* + * jQuery UI Effects Blind 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Blind + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.blind = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode + var direction = o.options.direction || 'vertical'; // Default direction + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper + var ref = (direction == 'vertical') ? 'height' : 'width'; + var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width(); + if(mode == 'show') wrapper.css(ref, 0); // Shift + + // Animation + var animation = {}; + animation[ref] = mode == 'show' ? distance : 0; + + // Animate + wrapper.animate(animation, o.duration, o.options.easing, function() { + if(mode == 'hide') el.hide(); // Hide + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(el[0], arguments); // Callback + el.dequeue(); + }); + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Bounce 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Bounce + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.bounce = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode + var direction = o.options.direction || 'up'; // Default direction + var distance = o.options.distance || 20; // Default distance + var times = o.options.times || 5; // Default # of times + var speed = o.duration || 250; // Default speed per bounce + if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + $.effects.createWrapper(el); // Create Wrapper + var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; + var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; + var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3); + if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift + if (mode == 'hide') distance = distance / (times * 2); + if (mode != 'hide') times--; + + // Animate + if (mode == 'show') { // Show Bounce + var animation = {opacity: 1}; + animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance; + el.animate(animation, speed / 2, o.options.easing); + distance = distance / 2; + times--; + }; + for (var i = 0; i < times; i++) { // Bounces + var animation1 = {}, animation2 = {}; + animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance; + animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance; + el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing); + distance = (mode == 'hide') ? distance * 2 : distance / 2; + }; + if (mode == 'hide') { // Last Bounce + var animation = {opacity: 0}; + animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance; + el.animate(animation, speed / 2, o.options.easing, function(){ + el.hide(); // Hide + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(this, arguments); // Callback + }); + } else { + var animation1 = {}, animation2 = {}; + animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance; + animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance; + el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){ + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(this, arguments); // Callback + }); + }; + el.queue('fx', function() { el.dequeue(); }); + el.dequeue(); + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Clip 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Clip + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.clip = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right','height','width']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode + var direction = o.options.direction || 'vertical'; // Default direction + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper + var animate = el[0].tagName == 'IMG' ? wrapper : el; + var ref = { + size: (direction == 'vertical') ? 'height' : 'width', + position: (direction == 'vertical') ? 'top' : 'left' + }; + var distance = (direction == 'vertical') ? animate.height() : animate.width(); + if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift + + // Animation + var animation = {}; + animation[ref.size] = mode == 'show' ? distance : 0; + animation[ref.position] = mode == 'show' ? 0 : distance / 2; + + // Animate + animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { + if(mode == 'hide') el.hide(); // Hide + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(el[0], arguments); // Callback + el.dequeue(); + }}); + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Drop 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Drop + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.drop = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right','opacity']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode + var direction = o.options.direction || 'left'; // Default Direction + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + $.effects.createWrapper(el); // Create Wrapper + var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; + var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; + var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 2 : el.outerWidth({margin:true}) / 2); + if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift + + // Animation + var animation = {opacity: mode == 'show' ? 1 : 0}; + animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance; + + // Animate + el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { + if(mode == 'hide') el.hide(); // Hide + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(this, arguments); // Callback + el.dequeue(); + }}); + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Explode 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Explode + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.explode = function(o) { + + return this.queue(function() { + + var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3; + var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3; + + o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode; + var el = $(this).show().css('visibility', 'hidden'); + var offset = el.offset(); + + //Substract the margins - not fixing the problem yet. + offset.top -= parseInt(el.css("marginTop"),10) || 0; + offset.left -= parseInt(el.css("marginLeft"),10) || 0; + + var width = el.outerWidth(true); + var height = el.outerHeight(true); + + for(var i=0;i') + .css({ + position: 'absolute', + visibility: 'visible', + left: -j*(width/cells), + top: -i*(height/rows) + }) + .parent() + .addClass('ui-effects-explode') + .css({ + position: 'absolute', + overflow: 'hidden', + width: width/cells, + height: height/rows, + left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0), + top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0), + opacity: o.options.mode == 'show' ? 0 : 1 + }).animate({ + left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)), + top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)), + opacity: o.options.mode == 'show' ? 1 : 0 + }, o.duration || 500); + } + } + + // Set a timeout, to call the callback approx. when the other animations have finished + setTimeout(function() { + + o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide(); + if(o.callback) o.callback.apply(el[0]); // Callback + el.dequeue(); + + $('div.ui-effects-explode').remove(); + + }, o.duration || 500); + + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Fade 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fade + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.fade = function(o) { + return this.queue(function() { + var elem = $(this), + mode = $.effects.setMode(elem, o.options.mode || 'hide'); + + elem.animate({ opacity: mode }, { + queue: false, + duration: o.duration, + easing: o.options.easing, + complete: function() { + (o.callback && o.callback.apply(this, arguments)); + elem.dequeue(); + } + }); + }); +}; + +})(jQuery); +/* + * jQuery UI Effects Fold 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.fold = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode + var size = o.options.size || 15; // Default fold size + var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value + var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2; + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper + var widthFirst = ((mode == 'show') != horizFirst); + var ref = widthFirst ? ['width', 'height'] : ['height', 'width']; + var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()]; + var percent = /([0-9]+)%/.exec(size); + if(percent) size = parseInt(percent[1],10) / 100 * distance[mode == 'hide' ? 0 : 1]; + if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift + + // Animation + var animation1 = {}, animation2 = {}; + animation1[ref[0]] = mode == 'show' ? distance[0] : size; + animation2[ref[1]] = mode == 'show' ? distance[1] : 0; + + // Animate + wrapper.animate(animation1, duration, o.options.easing) + .animate(animation2, duration, o.options.easing, function() { + if(mode == 'hide') el.hide(); // Hide + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(el[0], arguments); // Callback + el.dequeue(); + }); + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Highlight 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.highlight = function(o) { + return this.queue(function() { + var elem = $(this), + props = ['backgroundImage', 'backgroundColor', 'opacity'], + mode = $.effects.setMode(elem, o.options.mode || 'show'), + animation = { + backgroundColor: elem.css('backgroundColor') + }; + + if (mode == 'hide') { + animation.opacity = 0; + } + + $.effects.save(elem, props); + elem + .show() + .css({ + backgroundImage: 'none', + backgroundColor: o.options.color || '#ffff99' + }) + .animate(animation, { + queue: false, + duration: o.duration, + easing: o.options.easing, + complete: function() { + (mode == 'hide' && elem.hide()); + $.effects.restore(elem, props); + (mode == 'show' && !$.support.opacity && this.style.removeAttribute('filter')); + (o.callback && o.callback.apply(this, arguments)); + elem.dequeue(); + } + }); + }); +}; + +})(jQuery); +/* + * jQuery UI Effects Pulsate 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.pulsate = function(o) { + return this.queue(function() { + var elem = $(this), + mode = $.effects.setMode(elem, o.options.mode || 'show'); + times = ((o.options.times || 5) * 2) - 1; + duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2, + isVisible = elem.is(':visible'), + animateTo = 0; + + if (!isVisible) { + elem.css('opacity', 0).show(); + animateTo = 1; + } + + if ((mode == 'hide' && isVisible) || (mode == 'show' && !isVisible)) { + times--; + } + + for (var i = 0; i < times; i++) { + elem.animate({ opacity: animateTo }, duration, o.options.easing); + animateTo = (animateTo + 1) % 2; + } + + elem.animate({ opacity: animateTo }, duration, o.options.easing, function() { + if (animateTo == 0) { + elem.hide(); + } + (o.callback && o.callback.apply(this, arguments)); + }); + + elem + .queue('fx', function() { elem.dequeue(); }) + .dequeue(); + }); +}; + +})(jQuery); +/* + * jQuery UI Effects Scale 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Scale + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.puff = function(o) { + return this.queue(function() { + var elem = $(this), + mode = $.effects.setMode(elem, o.options.mode || 'hide'), + percent = parseInt(o.options.percent, 10) || 150, + factor = percent / 100, + original = { height: elem.height(), width: elem.width() }; + + $.extend(o.options, { + fade: true, + mode: mode, + percent: mode == 'hide' ? percent : 100, + from: mode == 'hide' + ? original + : { + height: original.height * factor, + width: original.width * factor + } + }); + + elem.effect('scale', o.options, o.duration, o.callback); + elem.dequeue(); + }); +}; + +$.effects.scale = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this); + + // Set options + var options = $.extend(true, {}, o.options); + var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode + var percent = parseInt(o.options.percent,10) || (parseInt(o.options.percent,10) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent + var direction = o.options.direction || 'both'; // Set default axis + var origin = o.options.origin; // The origin of the scaling + if (mode != 'effect') { // Set default origin and restore for show/hide + options.origin = origin || ['middle','center']; + options.restore = true; + } + var original = {height: el.height(), width: el.width()}; // Save original + el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state + + // Adjust + var factor = { // Set scaling factor + y: direction != 'horizontal' ? (percent / 100) : 1, + x: direction != 'vertical' ? (percent / 100) : 1 + }; + el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state + + if (o.options.fade) { // Fade option to support puff + if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;}; + if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;}; + }; + + // Animation + options.from = el.from; options.to = el.to; options.mode = mode; + + // Animate + el.effect('size', options, o.duration, o.callback); + el.dequeue(); + }); + +}; + +$.effects.size = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right','width','height','overflow','opacity']; + var props1 = ['position','top','bottom','left','right','overflow','opacity']; // Always restore + var props2 = ['width','height','overflow']; // Copy for children + var cProps = ['fontSize']; + var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom']; + var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode + var restore = o.options.restore || false; // Default restore + var scale = o.options.scale || 'both'; // Default scale mode + var origin = o.options.origin; // The origin of the sizing + var original = {height: el.height(), width: el.width()}; // Save original + el.from = o.options.from || original; // Default from state + el.to = o.options.to || original; // Default to state + // Adjust + if (origin) { // Calculate baseline shifts + var baseline = $.effects.getBaseline(origin, original); + el.from.top = (original.height - el.from.height) * baseline.y; + el.from.left = (original.width - el.from.width) * baseline.x; + el.to.top = (original.height - el.to.height) * baseline.y; + el.to.left = (original.width - el.to.width) * baseline.x; + }; + var factor = { // Set scaling factor + from: {y: el.from.height / original.height, x: el.from.width / original.width}, + to: {y: el.to.height / original.height, x: el.to.width / original.width} + }; + if (scale == 'box' || scale == 'both') { // Scale the css box + if (factor.from.y != factor.to.y) { // Vertical props scaling + props = props.concat(vProps); + el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from); + el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to); + }; + if (factor.from.x != factor.to.x) { // Horizontal props scaling + props = props.concat(hProps); + el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from); + el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to); + }; + }; + if (scale == 'content' || scale == 'both') { // Scale the content + if (factor.from.y != factor.to.y) { // Vertical props scaling + props = props.concat(cProps); + el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from); + el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to); + }; + }; + $.effects.save(el, restore ? props : props1); el.show(); // Save & Show + $.effects.createWrapper(el); // Create Wrapper + el.css('overflow','hidden').css(el.from); // Shift + + // Animate + if (scale == 'content' || scale == 'both') { // Scale the children + vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size + hProps = hProps.concat(['marginLeft','marginRight']); // Add margins + props2 = props.concat(vProps).concat(hProps); // Concat + el.find("*[width]").each(function(){ + child = $(this); + if (restore) $.effects.save(child, props2); + var c_original = {height: child.height(), width: child.width()}; // Save original + child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x}; + child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x}; + if (factor.from.y != factor.to.y) { // Vertical props scaling + child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from); + child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to); + }; + if (factor.from.x != factor.to.x) { // Horizontal props scaling + child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from); + child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to); + }; + child.css(child.from); // Shift children + child.animate(child.to, o.duration, o.options.easing, function(){ + if (restore) $.effects.restore(child, props2); // Restore children + }); // Animate children + }); + }; + + // Animate + el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { + if (el.to.opacity === 0) { + el.css('opacity', el.from.opacity); + } + if(mode == 'hide') el.hide(); // Hide + $.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(this, arguments); // Callback + el.dequeue(); + }}); + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Shake 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Shake + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.shake = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode + var direction = o.options.direction || 'left'; // Default direction + var distance = o.options.distance || 20; // Default distance + var times = o.options.times || 3; // Default # of times + var speed = o.duration || o.options.duration || 140; // Default speed per shake + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + $.effects.createWrapper(el); // Create Wrapper + var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; + var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; + + // Animation + var animation = {}, animation1 = {}, animation2 = {}; + animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance; + animation1[ref] = (motion == 'pos' ? '+=' : '-=') + distance * 2; + animation2[ref] = (motion == 'pos' ? '-=' : '+=') + distance * 2; + + // Animate + el.animate(animation, speed, o.options.easing); + for (var i = 1; i < times; i++) { // Shakes + el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing); + }; + el.animate(animation1, speed, o.options.easing). + animate(animation, speed / 2, o.options.easing, function(){ // Last shake + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(this, arguments); // Callback + }); + el.queue('fx', function() { el.dequeue(); }); + el.dequeue(); + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Slide 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Slide + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.slide = function(o) { + + return this.queue(function() { + + // Create element + var el = $(this), props = ['position','top','bottom','left','right']; + + // Set options + var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode + var direction = o.options.direction || 'left'; // Default Direction + + // Adjust + $.effects.save(el, props); el.show(); // Save & Show + $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper + var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left'; + var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg'; + var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true})); + if (mode == 'show') el.css(ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance); // Shift + + // Animation + var animation = {}; + animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance; + + // Animate + el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() { + if(mode == 'hide') el.hide(); // Hide + $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore + if(o.callback) o.callback.apply(this, arguments); // Callback + el.dequeue(); + }}); + + }); + +}; + +})(jQuery); +/* + * jQuery UI Effects Transfer 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Transfer + * + * Depends: + * jquery.effects.core.js + */ +(function( $, undefined ) { + +$.effects.transfer = function(o) { + return this.queue(function() { + var elem = $(this), + target = $(o.options.to), + endPosition = target.offset(), + animation = { + top: endPosition.top, + left: endPosition.left, + height: target.innerHeight(), + width: target.innerWidth() + }, + startPosition = elem.offset(), + transfer = $('
    ') + .appendTo(document.body) + .addClass(o.options.className) + .css({ + top: startPosition.top, + left: startPosition.left, + height: elem.innerHeight(), + width: elem.innerWidth(), + position: 'absolute' + }) + .animate(animation, o.duration, o.options.easing, function() { + transfer.remove(); + (o.callback && o.callback.apply(elem[0], arguments)); + elem.dequeue(); + }); + }); +}; + +})(jQuery); +/* + * jQuery UI Accordion 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget( "ui.accordion", { + options: { + active: 0, + animated: "slide", + autoHeight: true, + clearStyle: false, + collapsible: false, + event: "click", + fillSpace: false, + header: "> li > :first-child,> :not(li):even", + icons: { + header: "ui-icon-triangle-1-e", + headerSelected: "ui-icon-triangle-1-s" + }, + navigation: false, + navigationFilter: function() { + return this.href.toLowerCase() === location.href.toLowerCase(); + } + }, + + _create: function() { + var self = this, + options = self.options; + + self.running = 0; + + self.element + .addClass( "ui-accordion ui-widget ui-helper-reset" ) + // in lack of child-selectors in CSS + // we need to mark top-LIs in a UL-accordion for some IE-fix + .children( "li" ) + .addClass( "ui-accordion-li-fix" ); + + self.headers = self.element.find( options.header ) + .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" ) + .bind( "mouseenter.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).addClass( "ui-state-hover" ); + }) + .bind( "mouseleave.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).removeClass( "ui-state-hover" ); + }) + .bind( "focus.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).addClass( "ui-state-focus" ); + }) + .bind( "blur.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).removeClass( "ui-state-focus" ); + }); + + self.headers.next() + .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ); + + if ( options.navigation ) { + var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 ); + if ( current.length ) { + var header = current.closest( ".ui-accordion-header" ); + if ( header.length ) { + // anchor within header + self.active = header; + } else { + // anchor within content + self.active = current.closest( ".ui-accordion-content" ).prev(); + } + } + } + + self.active = self._findActive( self.active || options.active ) + .addClass( "ui-state-default ui-state-active" ) + .toggleClass( "ui-corner-all" ) + .toggleClass( "ui-corner-top" ); + self.active.next().addClass( "ui-accordion-content-active" ); + + self._createIcons(); + self.resize(); + + // ARIA + self.element.attr( "role", "tablist" ); + + self.headers + .attr( "role", "tab" ) + .bind( "keydown.accordion", function( event ) { + return self._keydown( event ); + }) + .next() + .attr( "role", "tabpanel" ); + + self.headers + .not( self.active || "" ) + .attr({ + "aria-expanded": "false", + "aria-selected": "false", + tabIndex: -1 + }) + .next() + .hide(); + + // make sure at least one header is in the tab order + if ( !self.active.length ) { + self.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + self.active + .attr({ + "aria-expanded": "true", + "aria-selected": "true", + tabIndex: 0 + }); + } + + // only need links in tab order for Safari + if ( !$.browser.safari ) { + self.headers.find( "a" ).attr( "tabIndex", -1 ); + } + + if ( options.event ) { + self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) { + self._clickHandler.call( self, event, this ); + event.preventDefault(); + }); + } + }, + + _createIcons: function() { + var options = this.options; + if ( options.icons ) { + $( "" ) + .addClass( "ui-icon " + options.icons.header ) + .prependTo( this.headers ); + this.active.children( ".ui-icon" ) + .toggleClass(options.icons.header) + .toggleClass(options.icons.headerSelected); + this.element.addClass( "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this.headers.children( ".ui-icon" ).remove(); + this.element.removeClass( "ui-accordion-icons" ); + }, + + destroy: function() { + var options = this.options; + + this.element + .removeClass( "ui-accordion ui-widget ui-helper-reset" ) + .removeAttr( "role" ); + + this.headers + .unbind( ".accordion" ) + .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) + .removeAttr( "role" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-selected" ) + .removeAttr( "tabIndex" ); + + this.headers.find( "a" ).removeAttr( "tabIndex" ); + this._destroyIcons(); + var contents = this.headers.next() + .css( "display", "" ) + .removeAttr( "role" ) + .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" ); + if ( options.autoHeight || options.fillHeight ) { + contents.css( "height", "" ); + } + + return $.Widget.prototype.destroy.call( this ); + }, + + _setOption: function( key, value ) { + $.Widget.prototype._setOption.apply( this, arguments ); + + if ( key == "active" ) { + this.activate( value ); + } + if ( key == "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + // #5332 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + if ( key == "disabled" ) { + this.headers.add(this.headers.next()) + [ value ? "addClass" : "removeClass" ]( + "ui-accordion-disabled ui-state-disabled" ); + } + }, + + _keydown: function( event ) { + if ( this.options.disabled || event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._clickHandler( { target: event.target }, event.target ); + event.preventDefault(); + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + toFocus.focus(); + return false; + } + + return true; + }, + + resize: function() { + var options = this.options, + maxHeight; + + if ( options.fillSpace ) { + if ( $.browser.msie ) { + var defOverflow = this.element.parent().css( "overflow" ); + this.element.parent().css( "overflow", "hidden"); + } + maxHeight = this.element.parent().height(); + if ($.browser.msie) { + this.element.parent().css( "overflow", defOverflow ); + } + + this.headers.each(function() { + maxHeight -= $( this ).outerHeight( true ); + }); + + this.headers.next() + .each(function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + }) + .css( "overflow", "auto" ); + } else if ( options.autoHeight ) { + maxHeight = 0; + this.headers.next() + .each(function() { + maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() ); + }) + .height( maxHeight ); + } + + return this; + }, + + activate: function( index ) { + // TODO this gets called on init, changing the option without an explicit call for that + this.options.active = index; + // call clickHandler with custom event + var active = this._findActive( index )[ 0 ]; + this._clickHandler( { target: active }, active ); + + return this; + }, + + _findActive: function( selector ) { + return selector + ? typeof selector === "number" + ? this.headers.filter( ":eq(" + selector + ")" ) + : this.headers.not( this.headers.not( selector ) ) + : selector === false + ? $( [] ) + : this.headers.filter( ":eq(0)" ); + }, + + // TODO isn't event.target enough? why the separate target argument? + _clickHandler: function( event, target ) { + var options = this.options; + if ( options.disabled ) { + return; + } + + // called only when using activate(false) to close all parts programmatically + if ( !event.target ) { + if ( !options.collapsible ) { + return; + } + this.active + .removeClass( "ui-state-active ui-corner-top" ) + .addClass( "ui-state-default ui-corner-all" ) + .children( ".ui-icon" ) + .removeClass( options.icons.headerSelected ) + .addClass( options.icons.header ); + this.active.next().addClass( "ui-accordion-content-active" ); + var toHide = this.active.next(), + data = { + options: options, + newHeader: $( [] ), + oldHeader: options.active, + newContent: $( [] ), + oldContent: toHide + }, + toShow = ( this.active = $( [] ) ); + this._toggle( toShow, toHide, data ); + return; + } + + // get the click target + var clicked = $( event.currentTarget || target ), + clickedIsActive = clicked[0] === this.active[0]; + + // TODO the option is changed, is that correct? + // TODO if it is correct, shouldn't that happen after determining that the click is valid? + options.active = options.collapsible && clickedIsActive ? + false : + this.headers.index( clicked ); + + // if animations are still active, or the active header is the target, ignore click + if ( this.running || ( !options.collapsible && clickedIsActive ) ) { + return; + } + + // find elements to show and hide + var active = this.active, + toShow = clicked.next(), + toHide = this.active.next(), + data = { + options: options, + newHeader: clickedIsActive && options.collapsible ? $([]) : clicked, + oldHeader: this.active, + newContent: clickedIsActive && options.collapsible ? $([]) : toShow, + oldContent: toHide + }, + down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] ); + + // when the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $([]) : clicked; + this._toggle( toShow, toHide, data, clickedIsActive, down ); + + // switch classes + active + .removeClass( "ui-state-active ui-corner-top" ) + .addClass( "ui-state-default ui-corner-all" ) + .children( ".ui-icon" ) + .removeClass( options.icons.headerSelected ) + .addClass( options.icons.header ); + if ( !clickedIsActive ) { + clicked + .removeClass( "ui-state-default ui-corner-all" ) + .addClass( "ui-state-active ui-corner-top" ) + .children( ".ui-icon" ) + .removeClass( options.icons.header ) + .addClass( options.icons.headerSelected ); + clicked + .next() + .addClass( "ui-accordion-content-active" ); + } + + return; + }, + + _toggle: function( toShow, toHide, data, clickedIsActive, down ) { + var self = this, + options = self.options; + + self.toShow = toShow; + self.toHide = toHide; + self.data = data; + + var complete = function() { + if ( !self ) { + return; + } + return self._completed.apply( self, arguments ); + }; + + // trigger changestart event + self._trigger( "changestart", null, self.data ); + + // count elements to animate + self.running = toHide.size() === 0 ? toShow.size() : toHide.size(); + + if ( options.animated ) { + var animOptions = {}; + + if ( options.collapsible && clickedIsActive ) { + animOptions = { + toShow: $( [] ), + toHide: toHide, + complete: complete, + down: down, + autoHeight: options.autoHeight || options.fillSpace + }; + } else { + animOptions = { + toShow: toShow, + toHide: toHide, + complete: complete, + down: down, + autoHeight: options.autoHeight || options.fillSpace + }; + } + + if ( !options.proxied ) { + options.proxied = options.animated; + } + + if ( !options.proxiedDuration ) { + options.proxiedDuration = options.duration; + } + + options.animated = $.isFunction( options.proxied ) ? + options.proxied( animOptions ) : + options.proxied; + + options.duration = $.isFunction( options.proxiedDuration ) ? + options.proxiedDuration( animOptions ) : + options.proxiedDuration; + + var animations = $.ui.accordion.animations, + duration = options.duration, + easing = options.animated; + + if ( easing && !animations[ easing ] && !$.easing[ easing ] ) { + easing = "slide"; + } + if ( !animations[ easing ] ) { + animations[ easing ] = function( options ) { + this.slide( options, { + easing: easing, + duration: duration || 700 + }); + }; + } + + animations[ easing ]( animOptions ); + } else { + if ( options.collapsible && clickedIsActive ) { + toShow.toggle(); + } else { + toHide.hide(); + toShow.show(); + } + + complete( true ); + } + + // TODO assert that the blur and focus triggers are really necessary, remove otherwise + toHide.prev() + .attr({ + "aria-expanded": "false", + "aria-selected": "false", + tabIndex: -1 + }) + .blur(); + toShow.prev() + .attr({ + "aria-expanded": "true", + "aria-selected": "true", + tabIndex: 0 + }) + .focus(); + }, + + _completed: function( cancel ) { + this.running = cancel ? 0 : --this.running; + if ( this.running ) { + return; + } + + if ( this.options.clearStyle ) { + this.toShow.add( this.toHide ).css({ + height: "", + overflow: "" + }); + } + + // other classes are removed before the animation; this one needs to stay until completed + this.toHide.removeClass( "ui-accordion-content-active" ); + // Work around for rendering bug in IE (#5421) + if ( this.toHide.length ) { + this.toHide.parent()[0].className = this.toHide.parent()[0].className; + } + + this._trigger( "change", null, this.data ); + } +}); + +$.extend( $.ui.accordion, { + version: "1.8.12", + animations: { + slide: function( options, additions ) { + options = $.extend({ + easing: "swing", + duration: 300 + }, options, additions ); + if ( !options.toHide.size() ) { + options.toShow.animate({ + height: "show", + paddingTop: "show", + paddingBottom: "show" + }, options ); + return; + } + if ( !options.toShow.size() ) { + options.toHide.animate({ + height: "hide", + paddingTop: "hide", + paddingBottom: "hide" + }, options ); + return; + } + var overflow = options.toShow.css( "overflow" ), + percentDone = 0, + showProps = {}, + hideProps = {}, + fxAttrs = [ "height", "paddingTop", "paddingBottom" ], + originalWidth; + // fix width before calculating height of hidden element + var s = options.toShow; + originalWidth = s[0].style.width; + s.width( parseInt( s.parent().width(), 10 ) + - parseInt( s.css( "paddingLeft" ), 10 ) + - parseInt( s.css( "paddingRight" ), 10 ) + - ( parseInt( s.css( "borderLeftWidth" ), 10 ) || 0 ) + - ( parseInt( s.css( "borderRightWidth" ), 10) || 0 ) ); + + $.each( fxAttrs, function( i, prop ) { + hideProps[ prop ] = "hide"; + + var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ ); + showProps[ prop ] = { + value: parts[ 1 ], + unit: parts[ 2 ] || "px" + }; + }); + options.toShow.css({ height: 0, overflow: "hidden" }).show(); + options.toHide + .filter( ":hidden" ) + .each( options.complete ) + .end() + .filter( ":visible" ) + .animate( hideProps, { + step: function( now, settings ) { + // only calculate the percent when animating height + // IE gets very inconsistent results when animating elements + // with small values, which is common for padding + if ( settings.prop == "height" ) { + percentDone = ( settings.end - settings.start === 0 ) ? 0 : + ( settings.now - settings.start ) / ( settings.end - settings.start ); + } + + options.toShow[ 0 ].style[ settings.prop ] = + ( percentDone * showProps[ settings.prop ].value ) + + showProps[ settings.prop ].unit; + }, + duration: options.duration, + easing: options.easing, + complete: function() { + if ( !options.autoHeight ) { + options.toShow.css( "height", "" ); + } + options.toShow.css({ + width: originalWidth, + overflow: overflow + }); + options.complete(); + } + }); + }, + bounceslide: function( options ) { + this.slide( options, { + easing: options.down ? "easeOutBounce" : "swing", + duration: options.down ? 1000 : 200 + }); + } + } +}); + +})( jQuery ); +/* + * jQuery UI Autocomplete 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Autocomplete + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js + */ +(function( $, undefined ) { + +// used to prevent race conditions with remote data sources +var requestIndex = 0; + +$.widget( "ui.autocomplete", { + options: { + appendTo: "body", + autoFocus: false, + delay: 300, + minLength: 1, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + source: null + }, + + pending: 0, + + _create: function() { + var self = this, + doc = this.element[ 0 ].ownerDocument, + suppressKeyPress; + + this.element + .addClass( "ui-autocomplete-input" ) + .attr( "autocomplete", "off" ) + // TODO verify these actually work as intended + .attr({ + role: "textbox", + "aria-autocomplete": "list", + "aria-haspopup": "true" + }) + .bind( "keydown.autocomplete", function( event ) { + if ( self.options.disabled || self.element.attr( "readonly" ) ) { + return; + } + + suppressKeyPress = false; + var keyCode = $.ui.keyCode; + switch( event.keyCode ) { + case keyCode.PAGE_UP: + self._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + self._move( "nextPage", event ); + break; + case keyCode.UP: + self._move( "previous", event ); + // prevent moving cursor to beginning of text field in some browsers + event.preventDefault(); + break; + case keyCode.DOWN: + self._move( "next", event ); + // prevent moving cursor to end of text field in some browsers + event.preventDefault(); + break; + case keyCode.ENTER: + case keyCode.NUMPAD_ENTER: + // when menu is open and has focus + if ( self.menu.active ) { + // #6055 - Opera still allows the keypress to occur + // which causes forms to submit + suppressKeyPress = true; + event.preventDefault(); + } + //passthrough - ENTER and TAB both select the current element + case keyCode.TAB: + if ( !self.menu.active ) { + return; + } + self.menu.select( event ); + break; + case keyCode.ESCAPE: + self.element.val( self.term ); + self.close( event ); + break; + default: + // keypress is triggered before the input value is changed + clearTimeout( self.searching ); + self.searching = setTimeout(function() { + // only search if the value has changed + if ( self.term != self.element.val() ) { + self.selectedItem = null; + self.search( null, event ); + } + }, self.options.delay ); + break; + } + }) + .bind( "keypress.autocomplete", function( event ) { + if ( suppressKeyPress ) { + suppressKeyPress = false; + event.preventDefault(); + } + }) + .bind( "focus.autocomplete", function() { + if ( self.options.disabled ) { + return; + } + + self.selectedItem = null; + self.previous = self.element.val(); + }) + .bind( "blur.autocomplete", function( event ) { + if ( self.options.disabled ) { + return; + } + + clearTimeout( self.searching ); + // clicks on the menu (or a button to trigger a search) will cause a blur event + self.closing = setTimeout(function() { + self.close( event ); + self._change( event ); + }, 150 ); + }); + this._initSource(); + this.response = function() { + return self._response.apply( self, arguments ); + }; + this.menu = $( "
      " ) + .addClass( "ui-autocomplete" ) + .appendTo( $( this.options.appendTo || "body", doc )[0] ) + // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown) + .mousedown(function( event ) { + // clicking on the scrollbar causes focus to shift to the body + // but we can't detect a mouseup or a click immediately afterward + // so we have to track the next mousedown and close the menu if + // the user clicks somewhere outside of the autocomplete + var menuElement = self.menu.element[ 0 ]; + if ( !$( event.target ).closest( ".ui-menu-item" ).length ) { + setTimeout(function() { + $( document ).one( 'mousedown', function( event ) { + if ( event.target !== self.element[ 0 ] && + event.target !== menuElement && + !$.ui.contains( menuElement, event.target ) ) { + self.close(); + } + }); + }, 1 ); + } + + // use another timeout to make sure the blur-event-handler on the input was already triggered + setTimeout(function() { + clearTimeout( self.closing ); + }, 13); + }) + .menu({ + focus: function( event, ui ) { + var item = ui.item.data( "item.autocomplete" ); + if ( false !== self._trigger( "focus", event, { item: item } ) ) { + // use value to match what will end up in the input, if it was a key event + if ( /^key/.test(event.originalEvent.type) ) { + self.element.val( item.value ); + } + } + }, + selected: function( event, ui ) { + var item = ui.item.data( "item.autocomplete" ), + previous = self.previous; + + // only trigger when focus was lost (click on menu) + if ( self.element[0] !== doc.activeElement ) { + self.element.focus(); + self.previous = previous; + // #6109 - IE triggers two focus events and the second + // is asynchronous, so we need to reset the previous + // term synchronously and asynchronously :-( + setTimeout(function() { + self.previous = previous; + self.selectedItem = item; + }, 1); + } + + if ( false !== self._trigger( "select", event, { item: item } ) ) { + self.element.val( item.value ); + } + // reset the term after the select event + // this allows custom select handling to work properly + self.term = self.element.val(); + + self.close( event ); + self.selectedItem = item; + }, + blur: function( event, ui ) { + // don't set the value of the text field if it's already correct + // this prevents moving the cursor unnecessarily + if ( self.menu.element.is(":visible") && + ( self.element.val() !== self.term ) ) { + self.element.val( self.term ); + } + } + }) + .zIndex( this.element.zIndex() + 1 ) + // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 + .css({ top: 0, left: 0 }) + .hide() + .data( "menu" ); + if ( $.fn.bgiframe ) { + this.menu.element.bgiframe(); + } + }, + + destroy: function() { + this.element + .removeClass( "ui-autocomplete-input" ) + .removeAttr( "autocomplete" ) + .removeAttr( "role" ) + .removeAttr( "aria-autocomplete" ) + .removeAttr( "aria-haspopup" ); + this.menu.element.remove(); + $.Widget.prototype.destroy.call( this ); + }, + + _setOption: function( key, value ) { + $.Widget.prototype._setOption.apply( this, arguments ); + if ( key === "source" ) { + this._initSource(); + } + if ( key === "appendTo" ) { + this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] ) + } + if ( key === "disabled" && value && this.xhr ) { + this.xhr.abort(); + } + }, + + _initSource: function() { + var self = this, + array, + url; + if ( $.isArray(this.options.source) ) { + array = this.options.source; + this.source = function( request, response ) { + response( $.ui.autocomplete.filter(array, request.term) ); + }; + } else if ( typeof this.options.source === "string" ) { + url = this.options.source; + this.source = function( request, response ) { + if ( self.xhr ) { + self.xhr.abort(); + } + self.xhr = $.ajax({ + url: url, + data: request, + dataType: "json", + autocompleteRequest: ++requestIndex, + success: function( data, status ) { + if ( this.autocompleteRequest === requestIndex ) { + response( data ); + } + }, + error: function() { + if ( this.autocompleteRequest === requestIndex ) { + response( [] ); + } + } + }); + }; + } else { + this.source = this.options.source; + } + }, + + search: function( value, event ) { + value = value != null ? value : this.element.val(); + + // always save the actual value, not the one passed as an argument + this.term = this.element.val(); + + if ( value.length < this.options.minLength ) { + return this.close( event ); + } + + clearTimeout( this.closing ); + if ( this._trigger( "search", event ) === false ) { + return; + } + + return this._search( value ); + }, + + _search: function( value ) { + this.pending++; + this.element.addClass( "ui-autocomplete-loading" ); + + this.source( { term: value }, this.response ); + }, + + _response: function( content ) { + if ( !this.options.disabled && content && content.length ) { + content = this._normalize( content ); + this._suggest( content ); + this._trigger( "open" ); + } else { + this.close(); + } + this.pending--; + if ( !this.pending ) { + this.element.removeClass( "ui-autocomplete-loading" ); + } + }, + + close: function( event ) { + clearTimeout( this.closing ); + if ( this.menu.element.is(":visible") ) { + this.menu.element.hide(); + this.menu.deactivate(); + this._trigger( "close", event ); + } + }, + + _change: function( event ) { + if ( this.previous !== this.element.val() ) { + this._trigger( "change", event, { item: this.selectedItem } ); + } + }, + + _normalize: function( items ) { + // assume all items have the right format when the first item is complete + if ( items.length && items[0].label && items[0].value ) { + return items; + } + return $.map( items, function(item) { + if ( typeof item === "string" ) { + return { + label: item, + value: item + }; + } + return $.extend({ + label: item.label || item.value, + value: item.value || item.label + }, item ); + }); + }, + + _suggest: function( items ) { + var ul = this.menu.element + .empty() + .zIndex( this.element.zIndex() + 1 ); + this._renderMenu( ul, items ); + // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate + this.menu.deactivate(); + this.menu.refresh(); + + // size and position menu + ul.show(); + this._resizeMenu(); + ul.position( $.extend({ + of: this.element + }, this.options.position )); + + if ( this.options.autoFocus ) { + this.menu.next( new $.Event("mouseover") ); + } + }, + + _resizeMenu: function() { + var ul = this.menu.element; + ul.outerWidth( Math.max( + ul.width( "" ).outerWidth(), + this.element.outerWidth() + ) ); + }, + + _renderMenu: function( ul, items ) { + var self = this; + $.each( items, function( index, item ) { + self._renderItem( ul, item ); + }); + }, + + _renderItem: function( ul, item) { + return $( "
    • " ) + .data( "item.autocomplete", item ) + .append( $( "" ).text( item.label ) ) + .appendTo( ul ); + }, + + _move: function( direction, event ) { + if ( !this.menu.element.is(":visible") ) { + this.search( null, event ); + return; + } + if ( this.menu.first() && /^previous/.test(direction) || + this.menu.last() && /^next/.test(direction) ) { + this.element.val( this.term ); + this.menu.deactivate(); + return; + } + this.menu[ direction ]( event ); + }, + + widget: function() { + return this.menu.element; + } +}); + +$.extend( $.ui.autocomplete, { + escapeRegex: function( value ) { + return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + }, + filter: function(array, term) { + var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); + return $.grep( array, function(value) { + return matcher.test( value.label || value.value || value ); + }); + } +}); + +}( jQuery )); + +/* + * jQuery UI Menu (not officially released) + * + * This widget isn't yet finished and the API is subject to change. We plan to finish + * it for the next release. You're welcome to give it a try anyway and give us feedback, + * as long as you're okay with migrating your code later on. We can help with that, too. + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Menu + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function($) { + +$.widget("ui.menu", { + _create: function() { + var self = this; + this.element + .addClass("ui-menu ui-widget ui-widget-content ui-corner-all") + .attr({ + role: "listbox", + "aria-activedescendant": "ui-active-menuitem" + }) + .click(function( event ) { + if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) { + return; + } + // temporary + event.preventDefault(); + self.select( event ); + }); + this.refresh(); + }, + + refresh: function() { + var self = this; + + // don't refresh list items that are already adapted + var items = this.element.children("li:not(.ui-menu-item):has(a)") + .addClass("ui-menu-item") + .attr("role", "menuitem"); + + items.children("a") + .addClass("ui-corner-all") + .attr("tabindex", -1) + // mouseenter doesn't work with event delegation + .mouseenter(function( event ) { + self.activate( event, $(this).parent() ); + }) + .mouseleave(function() { + self.deactivate(); + }); + }, + + activate: function( event, item ) { + this.deactivate(); + if (this.hasScroll()) { + var offset = item.offset().top - this.element.offset().top, + scroll = this.element.attr("scrollTop"), + elementHeight = this.element.height(); + if (offset < 0) { + this.element.attr("scrollTop", scroll + offset); + } else if (offset >= elementHeight) { + this.element.attr("scrollTop", scroll + offset - elementHeight + item.height()); + } + } + this.active = item.eq(0) + .children("a") + .addClass("ui-state-hover") + .attr("id", "ui-active-menuitem") + .end(); + this._trigger("focus", event, { item: item }); + }, + + deactivate: function() { + if (!this.active) { return; } + + this.active.children("a") + .removeClass("ui-state-hover") + .removeAttr("id"); + this._trigger("blur"); + this.active = null; + }, + + next: function(event) { + this.move("next", ".ui-menu-item:first", event); + }, + + previous: function(event) { + this.move("prev", ".ui-menu-item:last", event); + }, + + first: function() { + return this.active && !this.active.prevAll(".ui-menu-item").length; + }, + + last: function() { + return this.active && !this.active.nextAll(".ui-menu-item").length; + }, + + move: function(direction, edge, event) { + if (!this.active) { + this.activate(event, this.element.children(edge)); + return; + } + var next = this.active[direction + "All"](".ui-menu-item").eq(0); + if (next.length) { + this.activate(event, next); + } else { + this.activate(event, this.element.children(edge)); + } + }, + + // TODO merge with previousPage + nextPage: function(event) { + if (this.hasScroll()) { + // TODO merge with no-scroll-else + if (!this.active || this.last()) { + this.activate(event, this.element.children(".ui-menu-item:first")); + return; + } + var base = this.active.offset().top, + height = this.element.height(), + result = this.element.children(".ui-menu-item").filter(function() { + var close = $(this).offset().top - base - height + $(this).height(); + // TODO improve approximation + return close < 10 && close > -10; + }); + + // TODO try to catch this earlier when scrollTop indicates the last page anyway + if (!result.length) { + result = this.element.children(".ui-menu-item:last"); + } + this.activate(event, result); + } else { + this.activate(event, this.element.children(".ui-menu-item") + .filter(!this.active || this.last() ? ":first" : ":last")); + } + }, + + // TODO merge with nextPage + previousPage: function(event) { + if (this.hasScroll()) { + // TODO merge with no-scroll-else + if (!this.active || this.first()) { + this.activate(event, this.element.children(".ui-menu-item:last")); + return; + } + + var base = this.active.offset().top, + height = this.element.height(); + result = this.element.children(".ui-menu-item").filter(function() { + var close = $(this).offset().top - base + height - $(this).height(); + // TODO improve approximation + return close < 10 && close > -10; + }); + + // TODO try to catch this earlier when scrollTop indicates the last page anyway + if (!result.length) { + result = this.element.children(".ui-menu-item:first"); + } + this.activate(event, result); + } else { + this.activate(event, this.element.children(".ui-menu-item") + .filter(!this.active || this.first() ? ":last" : ":first")); + } + }, + + hasScroll: function() { + return this.element.height() < this.element.attr("scrollHeight"); + }, + + select: function( event ) { + this._trigger("selected", event, { item: this.active }); + } +}); + +}(jQuery)); +/* + * jQuery UI Button 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Button + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +var lastActive, + baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", + stateClasses = "ui-state-hover ui-state-active ", + typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", + formResetHandler = function( event ) { + $( ":ui-button", event.target.form ).each(function() { + var inst = $( this ).data( "button" ); + setTimeout(function() { + inst.refresh(); + }, 1 ); + }); + }, + radioGroup = function( radio ) { + var name = radio.name, + form = radio.form, + radios = $( [] ); + if ( name ) { + if ( form ) { + radios = $( form ).find( "[name='" + name + "']" ); + } else { + radios = $( "[name='" + name + "']", radio.ownerDocument ) + .filter(function() { + return !this.form; + }); + } + } + return radios; + }; + +$.widget( "ui.button", { + options: { + disabled: null, + text: true, + label: null, + icons: { + primary: null, + secondary: null + } + }, + _create: function() { + this.element.closest( "form" ) + .unbind( "reset.button" ) + .bind( "reset.button", formResetHandler ); + + if ( typeof this.options.disabled !== "boolean" ) { + this.options.disabled = this.element.attr( "disabled" ); + } + + this._determineButtonType(); + this.hasTitle = !!this.buttonElement.attr( "title" ); + + var self = this, + options = this.options, + toggleButton = this.type === "checkbox" || this.type === "radio", + hoverClass = "ui-state-hover" + ( !toggleButton ? " ui-state-active" : "" ), + focusClass = "ui-state-focus"; + + if ( options.label === null ) { + options.label = this.buttonElement.html(); + } + + if ( this.element.is( ":disabled" ) ) { + options.disabled = true; + } + + this.buttonElement + .addClass( baseClasses ) + .attr( "role", "button" ) + .bind( "mouseenter.button", function() { + if ( options.disabled ) { + return; + } + $( this ).addClass( "ui-state-hover" ); + if ( this === lastActive ) { + $( this ).addClass( "ui-state-active" ); + } + }) + .bind( "mouseleave.button", function() { + if ( options.disabled ) { + return; + } + $( this ).removeClass( hoverClass ); + }) + .bind( "focus.button", function() { + // no need to check disabled, focus won't be triggered anyway + $( this ).addClass( focusClass ); + }) + .bind( "blur.button", function() { + $( this ).removeClass( focusClass ); + }); + + if ( toggleButton ) { + this.element.bind( "change.button", function() { + self.refresh(); + }); + } + + if ( this.type === "checkbox" ) { + this.buttonElement.bind( "click.button", function() { + if ( options.disabled ) { + return false; + } + $( this ).toggleClass( "ui-state-active" ); + self.buttonElement.attr( "aria-pressed", self.element[0].checked ); + }); + } else if ( this.type === "radio" ) { + this.buttonElement.bind( "click.button", function() { + if ( options.disabled ) { + return false; + } + $( this ).addClass( "ui-state-active" ); + self.buttonElement.attr( "aria-pressed", true ); + + var radio = self.element[ 0 ]; + radioGroup( radio ) + .not( radio ) + .map(function() { + return $( this ).button( "widget" )[ 0 ]; + }) + .removeClass( "ui-state-active" ) + .attr( "aria-pressed", false ); + }); + } else { + this.buttonElement + .bind( "mousedown.button", function() { + if ( options.disabled ) { + return false; + } + $( this ).addClass( "ui-state-active" ); + lastActive = this; + $( document ).one( "mouseup", function() { + lastActive = null; + }); + }) + .bind( "mouseup.button", function() { + if ( options.disabled ) { + return false; + } + $( this ).removeClass( "ui-state-active" ); + }) + .bind( "keydown.button", function(event) { + if ( options.disabled ) { + return false; + } + if ( event.keyCode == $.ui.keyCode.SPACE || event.keyCode == $.ui.keyCode.ENTER ) { + $( this ).addClass( "ui-state-active" ); + } + }) + .bind( "keyup.button", function() { + $( this ).removeClass( "ui-state-active" ); + }); + + if ( this.buttonElement.is("a") ) { + this.buttonElement.keyup(function(event) { + if ( event.keyCode === $.ui.keyCode.SPACE ) { + // TODO pass through original event correctly (just as 2nd argument doesn't work) + $( this ).click(); + } + }); + } + } + + // TODO: pull out $.Widget's handling for the disabled option into + // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can + // be overridden by individual plugins + this._setOption( "disabled", options.disabled ); + }, + + _determineButtonType: function() { + + if ( this.element.is(":checkbox") ) { + this.type = "checkbox"; + } else if ( this.element.is(":radio") ) { + this.type = "radio"; + } else if ( this.element.is("input") ) { + this.type = "input"; + } else { + this.type = "button"; + } + + if ( this.type === "checkbox" || this.type === "radio" ) { + // we don't search against the document in case the element + // is disconnected from the DOM + var ancestor = this.element.parents().filter(":last"), + labelSelector = "label[for=" + this.element.attr("id") + "]"; + this.buttonElement = ancestor.find( labelSelector ); + if ( !this.buttonElement.length ) { + ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings(); + this.buttonElement = ancestor.filter( labelSelector ); + if ( !this.buttonElement.length ) { + this.buttonElement = ancestor.find( labelSelector ); + } + } + this.element.addClass( "ui-helper-hidden-accessible" ); + + var checked = this.element.is( ":checked" ); + if ( checked ) { + this.buttonElement.addClass( "ui-state-active" ); + } + this.buttonElement.attr( "aria-pressed", checked ); + } else { + this.buttonElement = this.element; + } + }, + + widget: function() { + return this.buttonElement; + }, + + destroy: function() { + this.element + .removeClass( "ui-helper-hidden-accessible" ); + this.buttonElement + .removeClass( baseClasses + " " + stateClasses + " " + typeClasses ) + .removeAttr( "role" ) + .removeAttr( "aria-pressed" ) + .html( this.buttonElement.find(".ui-button-text").html() ); + + if ( !this.hasTitle ) { + this.buttonElement.removeAttr( "title" ); + } + + $.Widget.prototype.destroy.call( this ); + }, + + _setOption: function( key, value ) { + $.Widget.prototype._setOption.apply( this, arguments ); + if ( key === "disabled" ) { + if ( value ) { + this.element.attr( "disabled", true ); + } else { + this.element.removeAttr( "disabled" ); + } + } + this._resetButton(); + }, + + refresh: function() { + var isDisabled = this.element.is( ":disabled" ); + if ( isDisabled !== this.options.disabled ) { + this._setOption( "disabled", isDisabled ); + } + if ( this.type === "radio" ) { + radioGroup( this.element[0] ).each(function() { + if ( $( this ).is( ":checked" ) ) { + $( this ).button( "widget" ) + .addClass( "ui-state-active" ) + .attr( "aria-pressed", true ); + } else { + $( this ).button( "widget" ) + .removeClass( "ui-state-active" ) + .attr( "aria-pressed", false ); + } + }); + } else if ( this.type === "checkbox" ) { + if ( this.element.is( ":checked" ) ) { + this.buttonElement + .addClass( "ui-state-active" ) + .attr( "aria-pressed", true ); + } else { + this.buttonElement + .removeClass( "ui-state-active" ) + .attr( "aria-pressed", false ); + } + } + }, + + _resetButton: function() { + if ( this.type === "input" ) { + if ( this.options.label ) { + this.element.val( this.options.label ); + } + return; + } + var buttonElement = this.buttonElement.removeClass( typeClasses ), + buttonText = $( "" ) + .addClass( "ui-button-text" ) + .html( this.options.label ) + .appendTo( buttonElement.empty() ) + .text(), + icons = this.options.icons, + multipleIcons = icons.primary && icons.secondary, + buttonClasses = []; + + if ( icons.primary || icons.secondary ) { + if ( this.options.text ) { + buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) ); + } + + if ( icons.primary ) { + buttonElement.prepend( "" ); + } + + if ( icons.secondary ) { + buttonElement.append( "" ); + } + + if ( !this.options.text ) { + buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" ); + + if ( !this.hasTitle ) { + buttonElement.attr( "title", buttonText ); + } + } + } else { + buttonClasses.push( "ui-button-text-only" ); + } + buttonElement.addClass( buttonClasses.join( " " ) ); + } +}); + +$.widget( "ui.buttonset", { + options: { + items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)" + }, + + _create: function() { + this.element.addClass( "ui-buttonset" ); + }, + + _init: function() { + this.refresh(); + }, + + _setOption: function( key, value ) { + if ( key === "disabled" ) { + this.buttons.button( "option", key, value ); + } + + $.Widget.prototype._setOption.apply( this, arguments ); + }, + + refresh: function() { + this.buttons = this.element.find( this.options.items ) + .filter( ":ui-button" ) + .button( "refresh" ) + .end() + .not( ":ui-button" ) + .button() + .end() + .map(function() { + return $( this ).button( "widget" )[ 0 ]; + }) + .removeClass( "ui-corner-all ui-corner-left ui-corner-right" ) + .filter( ":first" ) + .addClass( "ui-corner-left" ) + .end() + .filter( ":last" ) + .addClass( "ui-corner-right" ) + .end() + .end(); + }, + + destroy: function() { + this.element.removeClass( "ui-buttonset" ); + this.buttons + .map(function() { + return $( this ).button( "widget" )[ 0 ]; + }) + .removeClass( "ui-corner-left ui-corner-right" ) + .end() + .button( "destroy" ); + + $.Widget.prototype.destroy.call( this ); + } +}); + +}( jQuery ) ); +/* + * jQuery UI Datepicker 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker + * + * Depends: + * jquery.ui.core.js + */ +(function( $, undefined ) { + +$.extend($.ui, { datepicker: { version: "1.8.12" } }); + +var PROP_NAME = 'datepicker'; +var dpuuid = new Date().getTime(); + +/* Date picker manager. + Use the singleton instance of this class, $.datepicker, to interact with the date picker. + Settings for (groups of) date pickers are maintained in an instance object, + allowing multiple different settings on the same page. */ + +function Datepicker() { + this.debug = false; // Change this to true to start debugging + this._curInst = null; // The current instance in use + this._keyEvent = false; // If the last event was a key event + this._disabledInputs = []; // List of date picker inputs that have been disabled + this._datepickerShowing = false; // True if the popup picker is showing , false if not + this._inDialog = false; // True if showing within a "dialog", false if not + this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division + this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class + this._appendClass = 'ui-datepicker-append'; // The name of the append marker class + this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class + this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class + this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class + this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class + this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class + this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class + this.regional = []; // Available regional settings, indexed by language code + this.regional[''] = { // Default regional settings + closeText: 'Done', // Display text for close link + prevText: 'Prev', // Display text for previous month link + nextText: 'Next', // Display text for next month link + currentText: 'Today', // Display text for current month link + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], // Names of months for drop-down and formatting + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday + weekHeader: 'Wk', // Column header for week of the year + dateFormat: 'mm/dd/yy', // See format options on parseDate + firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... + isRTL: false, // True if right-to-left language, false if left-to-right + showMonthAfterYear: false, // True if the year select precedes month, false for month then year + yearSuffix: '' // Additional text to append to the year in the month headers + }; + this._defaults = { // Global defaults for all the date picker instances + showOn: 'focus', // 'focus' for popup on focus, + // 'button' for trigger button, or 'both' for either + showAnim: 'fadeIn', // Name of jQuery animation for popup + showOptions: {}, // Options for enhanced animations + defaultDate: null, // Used when field is blank: actual date, + // +/-number for offset from today, null for today + appendText: '', // Display text following the input box, e.g. showing the format + buttonText: '...', // Text for trigger button + buttonImage: '', // URL for trigger button image + buttonImageOnly: false, // True if the image appears alone, false if it appears on a button + hideIfNoPrevNext: false, // True to hide next/previous month links + // if not applicable, false to just disable them + navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links + gotoCurrent: false, // True if today link goes back to current selection instead + changeMonth: false, // True if month can be selected directly, false if only prev/next + changeYear: false, // True if year can be selected directly, false if only prev/next + yearRange: 'c-10:c+10', // Range of years to display in drop-down, + // either relative to today's year (-nn:+nn), relative to currently displayed year + // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) + showOtherMonths: false, // True to show dates in other months, false to leave blank + selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable + showWeek: false, // True to show week of the year, false to not show it + calculateWeek: this.iso8601Week, // How to calculate the week of the year, + // takes a Date and returns the number of the week for it + shortYearCutoff: '+10', // Short year values < this are in the current century, + // > this are in the previous century, + // string value starting with '+' for current year + value + minDate: null, // The earliest selectable date, or null for no limit + maxDate: null, // The latest selectable date, or null for no limit + duration: 'fast', // Duration of display/closure + beforeShowDay: null, // Function that takes a date and returns an array with + // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '', + // [2] = cell title (optional), e.g. $.datepicker.noWeekends + beforeShow: null, // Function that takes an input field and + // returns a set of custom settings for the date picker + onSelect: null, // Define a callback function when a date is selected + onChangeMonthYear: null, // Define a callback function when the month or year is changed + onClose: null, // Define a callback function when the datepicker is closed + numberOfMonths: 1, // Number of months to show at a time + showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) + stepMonths: 1, // Number of months to step back/forward + stepBigMonths: 12, // Number of months to step back/forward for the big links + altField: '', // Selector for an alternate field to store selected dates into + altFormat: '', // The date format to use for the alternate field + constrainInput: true, // The input is constrained by the current date format + showButtonPanel: false, // True to show button panel, false to not show it + autoSize: false // True to size the input for the date format, false to leave as is + }; + $.extend(this._defaults, this.regional['']); + this.dpDiv = $('
      '); +} + +$.extend(Datepicker.prototype, { + /* Class name added to elements to indicate already configured with a date picker. */ + markerClassName: 'hasDatepicker', + + /* Debug logging (if enabled). */ + log: function () { + if (this.debug) + console.log.apply('', arguments); + }, + + // TODO rename to "widget" when switching to widget factory + _widgetDatepicker: function() { + return this.dpDiv; + }, + + /* Override the default settings for all instances of the date picker. + @param settings object - the new settings to use as defaults (anonymous object) + @return the manager object */ + setDefaults: function(settings) { + extendRemove(this._defaults, settings || {}); + return this; + }, + + /* Attach the date picker to a jQuery selection. + @param target element - the target input field or division or span + @param settings object - the new settings to use for this date picker instance (anonymous) */ + _attachDatepicker: function(target, settings) { + // check for settings on the control itself - in namespace 'date:' + var inlineSettings = null; + for (var attrName in this._defaults) { + var attrValue = target.getAttribute('date:' + attrName); + if (attrValue) { + inlineSettings = inlineSettings || {}; + try { + inlineSettings[attrName] = eval(attrValue); + } catch (err) { + inlineSettings[attrName] = attrValue; + } + } + } + var nodeName = target.nodeName.toLowerCase(); + var inline = (nodeName == 'div' || nodeName == 'span'); + if (!target.id) { + this.uuid += 1; + target.id = 'dp' + this.uuid; + } + var inst = this._newInst($(target), inline); + inst.settings = $.extend({}, settings || {}, inlineSettings || {}); + if (nodeName == 'input') { + this._connectDatepicker(target, inst); + } else if (inline) { + this._inlineDatepicker(target, inst); + } + }, + + /* Create a new instance object. */ + _newInst: function(target, inline) { + var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars + return {id: id, input: target, // associated target + selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection + drawMonth: 0, drawYear: 0, // month being drawn + inline: inline, // is datepicker inline or not + dpDiv: (!inline ? this.dpDiv : // presentation div + $('
      '))}; + }, + + /* Attach the date picker to an input field. */ + _connectDatepicker: function(target, inst) { + var input = $(target); + inst.append = $([]); + inst.trigger = $([]); + if (input.hasClass(this.markerClassName)) + return; + this._attachments(input, inst); + input.addClass(this.markerClassName).keydown(this._doKeyDown). + keypress(this._doKeyPress).keyup(this._doKeyUp). + bind("setData.datepicker", function(event, key, value) { + inst.settings[key] = value; + }).bind("getData.datepicker", function(event, key) { + return this._get(inst, key); + }); + this._autoSize(inst); + $.data(target, PROP_NAME, inst); + }, + + /* Make attachments based on settings. */ + _attachments: function(input, inst) { + var appendText = this._get(inst, 'appendText'); + var isRTL = this._get(inst, 'isRTL'); + if (inst.append) + inst.append.remove(); + if (appendText) { + inst.append = $('' + appendText + ''); + input[isRTL ? 'before' : 'after'](inst.append); + } + input.unbind('focus', this._showDatepicker); + if (inst.trigger) + inst.trigger.remove(); + var showOn = this._get(inst, 'showOn'); + if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field + input.focus(this._showDatepicker); + if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked + var buttonText = this._get(inst, 'buttonText'); + var buttonImage = this._get(inst, 'buttonImage'); + inst.trigger = $(this._get(inst, 'buttonImageOnly') ? + $('').addClass(this._triggerClass). + attr({ src: buttonImage, alt: buttonText, title: buttonText }) : + $('').addClass(this._triggerClass). + html(buttonImage == '' ? buttonText : $('').attr( + { src:buttonImage, alt:buttonText, title:buttonText }))); + input[isRTL ? 'before' : 'after'](inst.trigger); + inst.trigger.click(function() { + if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0]) + $.datepicker._hideDatepicker(); + else + $.datepicker._showDatepicker(input[0]); + return false; + }); + } + }, + + /* Apply the maximum length for the date format. */ + _autoSize: function(inst) { + if (this._get(inst, 'autoSize') && !inst.inline) { + var date = new Date(2009, 12 - 1, 20); // Ensure double digits + var dateFormat = this._get(inst, 'dateFormat'); + if (dateFormat.match(/[DM]/)) { + var findMax = function(names) { + var max = 0; + var maxI = 0; + for (var i = 0; i < names.length; i++) { + if (names[i].length > max) { + max = names[i].length; + maxI = i; + } + } + return maxI; + }; + date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? + 'monthNames' : 'monthNamesShort')))); + date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? + 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay()); + } + inst.input.attr('size', this._formatDate(inst, date).length); + } + }, + + /* Attach an inline date picker to a div. */ + _inlineDatepicker: function(target, inst) { + var divSpan = $(target); + if (divSpan.hasClass(this.markerClassName)) + return; + divSpan.addClass(this.markerClassName).append(inst.dpDiv). + bind("setData.datepicker", function(event, key, value){ + inst.settings[key] = value; + }).bind("getData.datepicker", function(event, key){ + return this._get(inst, key); + }); + $.data(target, PROP_NAME, inst); + this._setDate(inst, this._getDefaultDate(inst), true); + this._updateDatepicker(inst); + this._updateAlternate(inst); + inst.dpDiv.show(); + }, + + /* Pop-up the date picker in a "dialog" box. + @param input element - ignored + @param date string or Date - the initial date to display + @param onSelect function - the function to call when a date is selected + @param settings object - update the dialog date picker instance's settings (anonymous object) + @param pos int[2] - coordinates for the dialog's position within the screen or + event - with x/y coordinates or + leave empty for default (screen centre) + @return the manager object */ + _dialogDatepicker: function(input, date, onSelect, settings, pos) { + var inst = this._dialogInst; // internal instance + if (!inst) { + this.uuid += 1; + var id = 'dp' + this.uuid; + this._dialogInput = $(''); + this._dialogInput.keydown(this._doKeyDown); + $('body').append(this._dialogInput); + inst = this._dialogInst = this._newInst(this._dialogInput, false); + inst.settings = {}; + $.data(this._dialogInput[0], PROP_NAME, inst); + } + extendRemove(inst.settings, settings || {}); + date = (date && date.constructor == Date ? this._formatDate(inst, date) : date); + this._dialogInput.val(date); + + this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); + if (!this._pos) { + var browserWidth = document.documentElement.clientWidth; + var browserHeight = document.documentElement.clientHeight; + var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollY = document.documentElement.scrollTop || document.body.scrollTop; + this._pos = // should use actual width/height below + [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; + } + + // move input on screen for focus, but hidden behind dialog + this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px'); + inst.settings.onSelect = onSelect; + this._inDialog = true; + this.dpDiv.addClass(this._dialogClass); + this._showDatepicker(this._dialogInput[0]); + if ($.blockUI) + $.blockUI(this.dpDiv); + $.data(this._dialogInput[0], PROP_NAME, inst); + return this; + }, + + /* Detach a datepicker from its control. + @param target element - the target input field or division or span */ + _destroyDatepicker: function(target) { + var $target = $(target); + var inst = $.data(target, PROP_NAME); + if (!$target.hasClass(this.markerClassName)) { + return; + } + var nodeName = target.nodeName.toLowerCase(); + $.removeData(target, PROP_NAME); + if (nodeName == 'input') { + inst.append.remove(); + inst.trigger.remove(); + $target.removeClass(this.markerClassName). + unbind('focus', this._showDatepicker). + unbind('keydown', this._doKeyDown). + unbind('keypress', this._doKeyPress). + unbind('keyup', this._doKeyUp); + } else if (nodeName == 'div' || nodeName == 'span') + $target.removeClass(this.markerClassName).empty(); + }, + + /* Enable the date picker to a jQuery selection. + @param target element - the target input field or division or span */ + _enableDatepicker: function(target) { + var $target = $(target); + var inst = $.data(target, PROP_NAME); + if (!$target.hasClass(this.markerClassName)) { + return; + } + var nodeName = target.nodeName.toLowerCase(); + if (nodeName == 'input') { + target.disabled = false; + inst.trigger.filter('button'). + each(function() { this.disabled = false; }).end(). + filter('img').css({opacity: '1.0', cursor: ''}); + } + else if (nodeName == 'div' || nodeName == 'span') { + var inline = $target.children('.' + this._inlineClass); + inline.children().removeClass('ui-state-disabled'); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value == target ? null : value); }); // delete entry + }, + + /* Disable the date picker to a jQuery selection. + @param target element - the target input field or division or span */ + _disableDatepicker: function(target) { + var $target = $(target); + var inst = $.data(target, PROP_NAME); + if (!$target.hasClass(this.markerClassName)) { + return; + } + var nodeName = target.nodeName.toLowerCase(); + if (nodeName == 'input') { + target.disabled = true; + inst.trigger.filter('button'). + each(function() { this.disabled = true; }).end(). + filter('img').css({opacity: '0.5', cursor: 'default'}); + } + else if (nodeName == 'div' || nodeName == 'span') { + var inline = $target.children('.' + this._inlineClass); + inline.children().addClass('ui-state-disabled'); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value == target ? null : value); }); // delete entry + this._disabledInputs[this._disabledInputs.length] = target; + }, + + /* Is the first field in a jQuery collection disabled as a datepicker? + @param target element - the target input field or division or span + @return boolean - true if disabled, false if enabled */ + _isDisabledDatepicker: function(target) { + if (!target) { + return false; + } + for (var i = 0; i < this._disabledInputs.length; i++) { + if (this._disabledInputs[i] == target) + return true; + } + return false; + }, + + /* Retrieve the instance data for the target control. + @param target element - the target input field or division or span + @return object - the associated instance data + @throws error if a jQuery problem getting data */ + _getInst: function(target) { + try { + return $.data(target, PROP_NAME); + } + catch (err) { + throw 'Missing instance data for this datepicker'; + } + }, + + /* Update or retrieve the settings for a date picker attached to an input field or division. + @param target element - the target input field or division or span + @param name object - the new settings to update or + string - the name of the setting to change or retrieve, + when retrieving also 'all' for all instance settings or + 'defaults' for all global defaults + @param value any - the new value for the setting + (omit if above is an object or to retrieve a value) */ + _optionDatepicker: function(target, name, value) { + var inst = this._getInst(target); + if (arguments.length == 2 && typeof name == 'string') { + return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) : + (inst ? (name == 'all' ? $.extend({}, inst.settings) : + this._get(inst, name)) : null)); + } + var settings = name || {}; + if (typeof name == 'string') { + settings = {}; + settings[name] = value; + } + if (inst) { + if (this._curInst == inst) { + this._hideDatepicker(); + } + var date = this._getDateDatepicker(target, true); + var minDate = this._getMinMaxDate(inst, 'min'); + var maxDate = this._getMinMaxDate(inst, 'max'); + extendRemove(inst.settings, settings); + // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided + if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined) + inst.settings.minDate = this._formatDate(inst, minDate); + if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined) + inst.settings.maxDate = this._formatDate(inst, maxDate); + this._attachments($(target), inst); + this._autoSize(inst); + this._setDateDatepicker(target, date); + this._updateDatepicker(inst); + } + }, + + // change method deprecated + _changeDatepicker: function(target, name, value) { + this._optionDatepicker(target, name, value); + }, + + /* Redraw the date picker attached to an input field or division. + @param target element - the target input field or division or span */ + _refreshDatepicker: function(target) { + var inst = this._getInst(target); + if (inst) { + this._updateDatepicker(inst); + } + }, + + /* Set the dates for a jQuery selection. + @param target element - the target input field or division or span + @param date Date - the new date */ + _setDateDatepicker: function(target, date) { + var inst = this._getInst(target); + if (inst) { + this._setDate(inst, date); + this._updateDatepicker(inst); + this._updateAlternate(inst); + } + }, + + /* Get the date(s) for the first entry in a jQuery selection. + @param target element - the target input field or division or span + @param noDefault boolean - true if no default date is to be used + @return Date - the current date */ + _getDateDatepicker: function(target, noDefault) { + var inst = this._getInst(target); + if (inst && !inst.inline) + this._setDateFromField(inst, noDefault); + return (inst ? this._getDate(inst) : null); + }, + + /* Handle keystrokes. */ + _doKeyDown: function(event) { + var inst = $.datepicker._getInst(event.target); + var handled = true; + var isRTL = inst.dpDiv.is('.ui-datepicker-rtl'); + inst._keyEvent = true; + if ($.datepicker._datepickerShowing) + switch (event.keyCode) { + case 9: $.datepicker._hideDatepicker(); + handled = false; + break; // hide on tab out + case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' + + $.datepicker._currentClass + ')', inst.dpDiv); + if (sel[0]) + $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); + else + $.datepicker._hideDatepicker(); + return false; // don't submit the form + break; // select the value on enter + case 27: $.datepicker._hideDatepicker(); + break; // hide on escape + case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, 'stepBigMonths') : + -$.datepicker._get(inst, 'stepMonths')), 'M'); + break; // previous month/year on page up/+ ctrl + case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, 'stepBigMonths') : + +$.datepicker._get(inst, 'stepMonths')), 'M'); + break; // next month/year on page down/+ ctrl + case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target); + handled = event.ctrlKey || event.metaKey; + break; // clear on ctrl or command +end + case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target); + handled = event.ctrlKey || event.metaKey; + break; // current on ctrl or command +home + case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D'); + handled = event.ctrlKey || event.metaKey; + // -1 day on ctrl or command +left + if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, 'stepBigMonths') : + -$.datepicker._get(inst, 'stepMonths')), 'M'); + // next month/year on alt +left on Mac + break; + case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D'); + handled = event.ctrlKey || event.metaKey; + break; // -1 week on ctrl or command +up + case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D'); + handled = event.ctrlKey || event.metaKey; + // +1 day on ctrl or command +right + if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, 'stepBigMonths') : + +$.datepicker._get(inst, 'stepMonths')), 'M'); + // next month/year on alt +right + break; + case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D'); + handled = event.ctrlKey || event.metaKey; + break; // +1 week on ctrl or command +down + default: handled = false; + } + else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home + $.datepicker._showDatepicker(this); + else { + handled = false; + } + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }, + + /* Filter entered characters - based on date format. */ + _doKeyPress: function(event) { + var inst = $.datepicker._getInst(event.target); + if ($.datepicker._get(inst, 'constrainInput')) { + var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')); + var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode); + return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1); + } + }, + + /* Synchronise manual entry and field/alternate field. */ + _doKeyUp: function(event) { + var inst = $.datepicker._getInst(event.target); + if (inst.input.val() != inst.lastVal) { + try { + var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'), + (inst.input ? inst.input.val() : null), + $.datepicker._getFormatConfig(inst)); + if (date) { // only if valid + $.datepicker._setDateFromField(inst); + $.datepicker._updateAlternate(inst); + $.datepicker._updateDatepicker(inst); + } + } + catch (event) { + $.datepicker.log(event); + } + } + return true; + }, + + /* Pop-up the date picker for a given input field. + @param input element - the input field attached to the date picker or + event - if triggered by focus */ + _showDatepicker: function(input) { + input = input.target || input; + if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger + input = $('input', input.parentNode)[0]; + if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here + return; + var inst = $.datepicker._getInst(input); + if ($.datepicker._curInst && $.datepicker._curInst != inst) { + $.datepicker._curInst.dpDiv.stop(true, true); + } + var beforeShow = $.datepicker._get(inst, 'beforeShow'); + extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); + inst.lastVal = null; + $.datepicker._lastInput = input; + $.datepicker._setDateFromField(inst); + if ($.datepicker._inDialog) // hide cursor + input.value = ''; + if (!$.datepicker._pos) { // position below input + $.datepicker._pos = $.datepicker._findPos(input); + $.datepicker._pos[1] += input.offsetHeight; // add the height + } + var isFixed = false; + $(input).parents().each(function() { + isFixed |= $(this).css('position') == 'fixed'; + return !isFixed; + }); + if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled + $.datepicker._pos[0] -= document.documentElement.scrollLeft; + $.datepicker._pos[1] -= document.documentElement.scrollTop; + } + var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; + $.datepicker._pos = null; + //to avoid flashes on Firefox + inst.dpDiv.empty(); + // determine sizing offscreen + inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'}); + $.datepicker._updateDatepicker(inst); + // fix width for dynamic number of date pickers + // and adjust position before showing + offset = $.datepicker._checkOffset(inst, offset, isFixed); + inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? + 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none', + left: offset.left + 'px', top: offset.top + 'px'}); + if (!inst.inline) { + var showAnim = $.datepicker._get(inst, 'showAnim'); + var duration = $.datepicker._get(inst, 'duration'); + var postProcess = function() { + $.datepicker._datepickerShowing = true; + var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only + if( !! cover.length ){ + var borders = $.datepicker._getBorders(inst.dpDiv); + cover.css({left: -borders[0], top: -borders[1], + width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()}); + } + }; + inst.dpDiv.zIndex($(input).zIndex()+1); + if ($.effects && $.effects[showAnim]) + inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess); + else + inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess); + if (!showAnim || !duration) + postProcess(); + if (inst.input.is(':visible') && !inst.input.is(':disabled')) + inst.input.focus(); + $.datepicker._curInst = inst; + } + }, + + /* Generate the date picker content. */ + _updateDatepicker: function(inst) { + var self = this; + var borders = $.datepicker._getBorders(inst.dpDiv); + inst.dpDiv.empty().append(this._generateHTML(inst)); + var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only + if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6 + cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()}) + } + inst.dpDiv.find('button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a') + .bind('mouseout', function(){ + $(this).removeClass('ui-state-hover'); + if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover'); + if(this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover'); + }) + .bind('mouseover', function(){ + if (!self._isDisabledDatepicker( inst.inline ? inst.dpDiv.parent()[0] : inst.input[0])) { + $(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover'); + $(this).addClass('ui-state-hover'); + if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover'); + if(this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover'); + } + }) + .end() + .find('.' + this._dayOverClass + ' a') + .trigger('mouseover') + .end(); + var numMonths = this._getNumberOfMonths(inst); + var cols = numMonths[1]; + var width = 17; + if (cols > 1) + inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em'); + else + inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width(''); + inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') + + 'Class']('ui-datepicker-multi'); + inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') + + 'Class']('ui-datepicker-rtl'); + if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input && + // #6694 - don't focus the input if it's already focused + // this breaks the change event in IE + inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement) + inst.input.focus(); + // deffered render of the years select (to avoid flashes on Firefox) + if( inst.yearshtml ){ + var origyearshtml = inst.yearshtml; + setTimeout(function(){ + //assure that inst.yearshtml didn't change. + if( origyearshtml === inst.yearshtml ){ + inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml); + } + origyearshtml = inst.yearshtml = null; + }, 0); + } + }, + + /* Retrieve the size of left and top borders for an element. + @param elem (jQuery object) the element of interest + @return (number[2]) the left and top borders */ + _getBorders: function(elem) { + var convert = function(value) { + return {thin: 1, medium: 2, thick: 3}[value] || value; + }; + return [parseFloat(convert(elem.css('border-left-width'))), + parseFloat(convert(elem.css('border-top-width')))]; + }, + + /* Check positioning to remain on screen. */ + _checkOffset: function(inst, offset, isFixed) { + var dpWidth = inst.dpDiv.outerWidth(); + var dpHeight = inst.dpDiv.outerHeight(); + var inputWidth = inst.input ? inst.input.outerWidth() : 0; + var inputHeight = inst.input ? inst.input.outerHeight() : 0; + var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft(); + var viewHeight = document.documentElement.clientHeight + $(document).scrollTop(); + + offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0); + offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0; + offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; + + // now check if datepicker is showing outside window viewport - move to a better place if so. + offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); + offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + Math.abs(dpHeight + inputHeight) : 0); + + return offset; + }, + + /* Find an object's position on the screen. */ + _findPos: function(obj) { + var inst = this._getInst(obj); + var isRTL = this._get(inst, 'isRTL'); + while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) { + obj = obj[isRTL ? 'previousSibling' : 'nextSibling']; + } + var position = $(obj).offset(); + return [position.left, position.top]; + }, + + /* Hide the date picker from view. + @param input element - the input field attached to the date picker */ + _hideDatepicker: function(input) { + var inst = this._curInst; + if (!inst || (input && inst != $.data(input, PROP_NAME))) + return; + if (this._datepickerShowing) { + var showAnim = this._get(inst, 'showAnim'); + var duration = this._get(inst, 'duration'); + var postProcess = function() { + $.datepicker._tidyDialog(inst); + this._curInst = null; + }; + if ($.effects && $.effects[showAnim]) + inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess); + else + inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' : + (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess); + if (!showAnim) + postProcess(); + var onClose = this._get(inst, 'onClose'); + if (onClose) + onClose.apply((inst.input ? inst.input[0] : null), + [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback + this._datepickerShowing = false; + this._lastInput = null; + if (this._inDialog) { + this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' }); + if ($.blockUI) { + $.unblockUI(); + $('body').append(this.dpDiv); + } + } + this._inDialog = false; + } + }, + + /* Tidy up after a dialog display. */ + _tidyDialog: function(inst) { + inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar'); + }, + + /* Close date picker if clicked elsewhere. */ + _checkExternalClick: function(event) { + if (!$.datepicker._curInst) + return; + var $target = $(event.target); + if ($target[0].id != $.datepicker._mainDivId && + $target.parents('#' + $.datepicker._mainDivId).length == 0 && + !$target.hasClass($.datepicker.markerClassName) && + !$target.hasClass($.datepicker._triggerClass) && + $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI)) + $.datepicker._hideDatepicker(); + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function(id, offset, period) { + var target = $(id); + var inst = this._getInst(target[0]); + if (this._isDisabledDatepicker(target[0])) { + return; + } + this._adjustInstDate(inst, offset + + (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning + period); + this._updateDatepicker(inst); + }, + + /* Action for current link. */ + _gotoToday: function(id) { + var target = $(id); + var inst = this._getInst(target[0]); + if (this._get(inst, 'gotoCurrent') && inst.currentDay) { + inst.selectedDay = inst.currentDay; + inst.drawMonth = inst.selectedMonth = inst.currentMonth; + inst.drawYear = inst.selectedYear = inst.currentYear; + } + else { + var date = new Date(); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + } + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a new month/year. */ + _selectMonthYear: function(id, select, period) { + var target = $(id); + var inst = this._getInst(target[0]); + inst._selectingMonthYear = false; + inst['selected' + (period == 'M' ? 'Month' : 'Year')] = + inst['draw' + (period == 'M' ? 'Month' : 'Year')] = + parseInt(select.options[select.selectedIndex].value,10); + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Restore input focus after not changing month/year. */ + _clickMonthYear: function(id) { + var target = $(id); + var inst = this._getInst(target[0]); + if (inst.input && inst._selectingMonthYear) { + setTimeout(function() { + inst.input.focus(); + }, 0); + } + inst._selectingMonthYear = !inst._selectingMonthYear; + }, + + /* Action for selecting a day. */ + _selectDay: function(id, month, year, td) { + var target = $(id); + if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { + return; + } + var inst = this._getInst(target[0]); + inst.selectedDay = inst.currentDay = $('a', td).html(); + inst.selectedMonth = inst.currentMonth = month; + inst.selectedYear = inst.currentYear = year; + this._selectDate(id, this._formatDate(inst, + inst.currentDay, inst.currentMonth, inst.currentYear)); + }, + + /* Erase the input field and hide the date picker. */ + _clearDate: function(id) { + var target = $(id); + var inst = this._getInst(target[0]); + this._selectDate(target, ''); + }, + + /* Update the input field with the selected date. */ + _selectDate: function(id, dateStr) { + var target = $(id); + var inst = this._getInst(target[0]); + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (inst.input) + inst.input.val(dateStr); + this._updateAlternate(inst); + var onSelect = this._get(inst, 'onSelect'); + if (onSelect) + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback + else if (inst.input) + inst.input.trigger('change'); // fire the change event + if (inst.inline) + this._updateDatepicker(inst); + else { + this._hideDatepicker(); + this._lastInput = inst.input[0]; + if (typeof(inst.input[0]) != 'object') + inst.input.focus(); // restore focus + this._lastInput = null; + } + }, + + /* Update any alternate field to synchronise with the main field. */ + _updateAlternate: function(inst) { + var altField = this._get(inst, 'altField'); + if (altField) { // update alternate field too + var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat'); + var date = this._getDate(inst); + var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); + $(altField).each(function() { $(this).val(dateStr); }); + } + }, + + /* Set as beforeShowDay function to prevent selection of weekends. + @param date Date - the date to customise + @return [boolean, string] - is this date selectable?, what is its CSS class? */ + noWeekends: function(date) { + var day = date.getDay(); + return [(day > 0 && day < 6), '']; + }, + + /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. + @param date Date - the date to get the week for + @return number - the number of the week within the year that contains this date */ + iso8601Week: function(date) { + var checkDate = new Date(date.getTime()); + // Find Thursday of this week starting on Monday + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); + var time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + }, + + /* Parse a string value into a date object. + See formatDate below for the possible formats. + + @param format string - the expected format of the date + @param value string - the date in the above format + @param settings Object - attributes include: + shortYearCutoff number - the cutoff year for determining the century (optional) + dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + dayNames string[7] - names of the days from Sunday (optional) + monthNamesShort string[12] - abbreviated names of the months (optional) + monthNames string[12] - names of the months (optional) + @return Date - the extracted date value or null if value is blank */ + parseDate: function (format, value, settings) { + if (format == null || value == null) + throw 'Invalid arguments'; + value = (typeof value == 'object' ? value.toString() : value + ''); + if (value == '') + return null; + var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff; + shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff : + new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); + var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; + var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; + var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; + var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; + var year = -1; + var month = -1; + var day = -1; + var doy = -1; + var literal = false; + // Check whether a format character is doubled + var lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); + if (matches) + iFormat++; + return matches; + }; + // Extract a number from the string value + var getNumber = function(match) { + var isDoubled = lookAhead(match); + var size = (match == '@' ? 14 : (match == '!' ? 20 : + (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2)))); + var digits = new RegExp('^\\d{1,' + size + '}'); + var num = value.substring(iValue).match(digits); + if (!num) + throw 'Missing number at position ' + iValue; + iValue += num[0].length; + return parseInt(num[0], 10); + }; + // Extract a name from the string value and convert to an index + var getName = function(match, shortNames, longNames) { + var names = (lookAhead(match) ? longNames : shortNames); + for (var i = 0; i < names.length; i++) { + if (value.substr(iValue, names[i].length).toLowerCase() == names[i].toLowerCase()) { + iValue += names[i].length; + return i + 1; + } + } + throw 'Unknown name at position ' + iValue; + }; + // Confirm that a literal character matches the string value + var checkLiteral = function() { + if (value.charAt(iValue) != format.charAt(iFormat)) + throw 'Unexpected literal at position ' + iValue; + iValue++; + }; + var iValue = 0; + for (var iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) + if (format.charAt(iFormat) == "'" && !lookAhead("'")) + literal = false; + else + checkLiteral(); + else + switch (format.charAt(iFormat)) { + case 'd': + day = getNumber('d'); + break; + case 'D': + getName('D', dayNamesShort, dayNames); + break; + case 'o': + doy = getNumber('o'); + break; + case 'm': + month = getNumber('m'); + break; + case 'M': + month = getName('M', monthNamesShort, monthNames); + break; + case 'y': + year = getNumber('y'); + break; + case '@': + var date = new Date(getNumber('@')); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case '!': + var date = new Date((getNumber('!') - this._ticksTo1970) / 10000); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "'": + if (lookAhead("'")) + checkLiteral(); + else + literal = true; + break; + default: + checkLiteral(); + } + } + if (year == -1) + year = new Date().getFullYear(); + else if (year < 100) + year += new Date().getFullYear() - new Date().getFullYear() % 100 + + (year <= shortYearCutoff ? 0 : -100); + if (doy > -1) { + month = 1; + day = doy; + do { + var dim = this._getDaysInMonth(year, month - 1); + if (day <= dim) + break; + month++; + day -= dim; + } while (true); + } + var date = this._daylightSavingAdjust(new Date(year, month - 1, day)); + if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) + throw 'Invalid date'; // E.g. 31/02/00 + return date; + }, + + /* Standard date formats. */ + ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601) + COOKIE: 'D, dd M yy', + ISO_8601: 'yy-mm-dd', + RFC_822: 'D, d M y', + RFC_850: 'DD, dd-M-y', + RFC_1036: 'D, d M y', + RFC_1123: 'D, d M yy', + RFC_2822: 'D, d M yy', + RSS: 'D, d M y', // RFC 822 + TICKS: '!', + TIMESTAMP: '@', + W3C: 'yy-mm-dd', // ISO 8601 + + _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + + Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), + + /* Format a date object into a string value. + The format can be combinations of the following: + d - day of month (no leading zero) + dd - day of month (two digit) + o - day of year (no leading zeros) + oo - day of year (three digit) + D - day name short + DD - day name long + m - month of year (no leading zero) + mm - month of year (two digit) + M - month name short + MM - month name long + y - year (two digit) + yy - year (four digit) + @ - Unix timestamp (ms since 01/01/1970) + ! - Windows ticks (100ns since 01/01/0001) + '...' - literal text + '' - single quote + + @param format string - the desired format of the date + @param date Date - the date value to format + @param settings Object - attributes include: + dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + dayNames string[7] - names of the days from Sunday (optional) + monthNamesShort string[12] - abbreviated names of the months (optional) + monthNames string[12] - names of the months (optional) + @return string - the date in the above format */ + formatDate: function (format, date, settings) { + if (!date) + return ''; + var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; + var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; + var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; + var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; + // Check whether a format character is doubled + var lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); + if (matches) + iFormat++; + return matches; + }; + // Format a number, with leading zero if necessary + var formatNumber = function(match, value, len) { + var num = '' + value; + if (lookAhead(match)) + while (num.length < len) + num = '0' + num; + return num; + }; + // Format a name, short or long as requested + var formatName = function(match, value, shortNames, longNames) { + return (lookAhead(match) ? longNames[value] : shortNames[value]); + }; + var output = ''; + var literal = false; + if (date) + for (var iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) + if (format.charAt(iFormat) == "'" && !lookAhead("'")) + literal = false; + else + output += format.charAt(iFormat); + else + switch (format.charAt(iFormat)) { + case 'd': + output += formatNumber('d', date.getDate(), 2); + break; + case 'D': + output += formatName('D', date.getDay(), dayNamesShort, dayNames); + break; + case 'o': + output += formatNumber('o', + (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3); + break; + case 'm': + output += formatNumber('m', date.getMonth() + 1, 2); + break; + case 'M': + output += formatName('M', date.getMonth(), monthNamesShort, monthNames); + break; + case 'y': + output += (lookAhead('y') ? date.getFullYear() : + (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100); + break; + case '@': + output += date.getTime(); + break; + case '!': + output += date.getTime() * 10000 + this._ticksTo1970; + break; + case "'": + if (lookAhead("'")) + output += "'"; + else + literal = true; + break; + default: + output += format.charAt(iFormat); + } + } + return output; + }, + + /* Extract all possible characters from the date format. */ + _possibleChars: function (format) { + var chars = ''; + var literal = false; + // Check whether a format character is doubled + var lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); + if (matches) + iFormat++; + return matches; + }; + for (var iFormat = 0; iFormat < format.length; iFormat++) + if (literal) + if (format.charAt(iFormat) == "'" && !lookAhead("'")) + literal = false; + else + chars += format.charAt(iFormat); + else + switch (format.charAt(iFormat)) { + case 'd': case 'm': case 'y': case '@': + chars += '0123456789'; + break; + case 'D': case 'M': + return null; // Accept anything + case "'": + if (lookAhead("'")) + chars += "'"; + else + literal = true; + break; + default: + chars += format.charAt(iFormat); + } + return chars; + }, + + /* Get a setting value, defaulting if necessary. */ + _get: function(inst, name) { + return inst.settings[name] !== undefined ? + inst.settings[name] : this._defaults[name]; + }, + + /* Parse existing date and initialise date picker. */ + _setDateFromField: function(inst, noDefault) { + if (inst.input.val() == inst.lastVal) { + return; + } + var dateFormat = this._get(inst, 'dateFormat'); + var dates = inst.lastVal = inst.input ? inst.input.val() : null; + var date, defaultDate; + date = defaultDate = this._getDefaultDate(inst); + var settings = this._getFormatConfig(inst); + try { + date = this.parseDate(dateFormat, dates, settings) || defaultDate; + } catch (event) { + this.log(event); + dates = (noDefault ? '' : dates); + } + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + inst.currentDay = (dates ? date.getDate() : 0); + inst.currentMonth = (dates ? date.getMonth() : 0); + inst.currentYear = (dates ? date.getFullYear() : 0); + this._adjustInstDate(inst); + }, + + /* Retrieve the default date shown on opening. */ + _getDefaultDate: function(inst) { + return this._restrictMinMax(inst, + this._determineDate(inst, this._get(inst, 'defaultDate'), new Date())); + }, + + /* A date may be specified as an exact value or a relative one. */ + _determineDate: function(inst, date, defaultDate) { + var offsetNumeric = function(offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + return date; + }; + var offsetString = function(offset) { + try { + return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'), + offset, $.datepicker._getFormatConfig(inst)); + } + catch (e) { + // Ignore + } + var date = (offset.toLowerCase().match(/^c/) ? + $.datepicker._getDate(inst) : null) || new Date(); + var year = date.getFullYear(); + var month = date.getMonth(); + var day = date.getDate(); + var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g; + var matches = pattern.exec(offset); + while (matches) { + switch (matches[2] || 'd') { + case 'd' : case 'D' : + day += parseInt(matches[1],10); break; + case 'w' : case 'W' : + day += parseInt(matches[1],10) * 7; break; + case 'm' : case 'M' : + month += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + case 'y': case 'Y' : + year += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + } + matches = pattern.exec(offset); + } + return new Date(year, month, day); + }; + var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) : + (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); + newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate); + if (newDate) { + newDate.setHours(0); + newDate.setMinutes(0); + newDate.setSeconds(0); + newDate.setMilliseconds(0); + } + return this._daylightSavingAdjust(newDate); + }, + + /* Handle switch to/from daylight saving. + Hours may be non-zero on daylight saving cut-over: + > 12 when midnight changeover, but then cannot generate + midnight datetime, so jump to 1AM, otherwise reset. + @param date (Date) the date to check + @return (Date) the corrected date */ + _daylightSavingAdjust: function(date) { + if (!date) return null; + date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); + return date; + }, + + /* Set the date(s) directly. */ + _setDate: function(inst, date, noChange) { + var clear = !date; + var origMonth = inst.selectedMonth; + var origYear = inst.selectedYear; + var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); + inst.selectedDay = inst.currentDay = newDate.getDate(); + inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); + inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); + if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange) + this._notifyChange(inst); + this._adjustInstDate(inst); + if (inst.input) { + inst.input.val(clear ? '' : this._formatDate(inst)); + } + }, + + /* Retrieve the date(s) directly. */ + _getDate: function(inst) { + var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null : + this._daylightSavingAdjust(new Date( + inst.currentYear, inst.currentMonth, inst.currentDay))); + return startDate; + }, + + /* Generate the HTML for the current state of the date picker. */ + _generateHTML: function(inst) { + var today = new Date(); + today = this._daylightSavingAdjust( + new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time + var isRTL = this._get(inst, 'isRTL'); + var showButtonPanel = this._get(inst, 'showButtonPanel'); + var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext'); + var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat'); + var numMonths = this._getNumberOfMonths(inst); + var showCurrentAtPos = this._get(inst, 'showCurrentAtPos'); + var stepMonths = this._get(inst, 'stepMonths'); + var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1); + var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : + new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); + var minDate = this._getMinMaxDate(inst, 'min'); + var maxDate = this._getMinMaxDate(inst, 'max'); + var drawMonth = inst.drawMonth - showCurrentAtPos; + var drawYear = inst.drawYear; + if (drawMonth < 0) { + drawMonth += 12; + drawYear--; + } + if (maxDate) { + var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), + maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); + maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); + while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { + drawMonth--; + if (drawMonth < 0) { + drawMonth = 11; + drawYear--; + } + } + } + inst.drawMonth = drawMonth; + inst.drawYear = drawYear; + var prevText = this._get(inst, 'prevText'); + prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), + this._getFormatConfig(inst))); + var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? + '' + prevText + '' : + (hideIfNoPrevNext ? '' : '' + prevText + '')); + var nextText = this._get(inst, 'nextText'); + nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), + this._getFormatConfig(inst))); + var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? + '' + nextText + '' : + (hideIfNoPrevNext ? '' : '' + nextText + '')); + var currentText = this._get(inst, 'currentText'); + var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today); + currentText = (!navigationAsDateFormat ? currentText : + this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); + var controls = (!inst.inline ? '' : ''); + var buttonPanel = (showButtonPanel) ? '
      ' + (isRTL ? controls : '') + + (this._isInRange(inst, gotoDate) ? '' : '') + (isRTL ? '' : controls) + '
      ' : ''; + var firstDay = parseInt(this._get(inst, 'firstDay'),10); + firstDay = (isNaN(firstDay) ? 0 : firstDay); + var showWeek = this._get(inst, 'showWeek'); + var dayNames = this._get(inst, 'dayNames'); + var dayNamesShort = this._get(inst, 'dayNamesShort'); + var dayNamesMin = this._get(inst, 'dayNamesMin'); + var monthNames = this._get(inst, 'monthNames'); + var monthNamesShort = this._get(inst, 'monthNamesShort'); + var beforeShowDay = this._get(inst, 'beforeShowDay'); + var showOtherMonths = this._get(inst, 'showOtherMonths'); + var selectOtherMonths = this._get(inst, 'selectOtherMonths'); + var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week; + var defaultDate = this._getDefaultDate(inst); + var html = ''; + for (var row = 0; row < numMonths[0]; row++) { + var group = ''; + for (var col = 0; col < numMonths[1]; col++) { + var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); + var cornerClass = ' ui-corner-all'; + var calender = ''; + if (isMultiMonth) { + calender += '
      '; + } + calender += '
      ' + + (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') + + (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') + + this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, + row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers + '
      ' + + ''; + var thead = (showWeek ? '' : ''); + for (var dow = 0; dow < 7; dow++) { // days of the week + var day = (dow + firstDay) % 7; + thead += '= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' + + '' + dayNamesMin[day] + ''; + } + calender += thead + ''; + var daysInMonth = this._getDaysInMonth(drawYear, drawMonth); + if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth) + inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); + var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; + var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate + var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); + for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows + calender += ''; + var tbody = (!showWeek ? '' : ''); + for (var dow = 0; dow < 7; dow++) { // create date picker days + var daySettings = (beforeShowDay ? + beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']); + var otherMonth = (printDate.getMonth() != drawMonth); + var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || + (minDate && printDate < minDate) || (maxDate && printDate > maxDate); + tbody += ''; // display selectable date + printDate.setDate(printDate.getDate() + 1); + printDate = this._daylightSavingAdjust(printDate); + } + calender += tbody + ''; + } + drawMonth++; + if (drawMonth > 11) { + drawMonth = 0; + drawYear++; + } + calender += '
      ' + this._get(inst, 'weekHeader') + '
      ' + + this._get(inst, 'calculateWeek')(printDate) + '' + // actions + (otherMonth && !showOtherMonths ? ' ' : // display for other months + (unselectable ? '' + printDate.getDate() + '' : '' + printDate.getDate() + '')) + '
      ' + (isMultiMonth ? '
      ' + + ((numMonths[0] > 0 && col == numMonths[1]-1) ? '
      ' : '') : ''); + group += calender; + } + html += group; + } + html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ? + '' : ''); + inst._keyEvent = false; + return html; + }, + + /* Generate the month and year header. */ + _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, + secondary, monthNames, monthNamesShort) { + var changeMonth = this._get(inst, 'changeMonth'); + var changeYear = this._get(inst, 'changeYear'); + var showMonthAfterYear = this._get(inst, 'showMonthAfterYear'); + var html = '
      '; + var monthHtml = ''; + // month selection + if (secondary || !changeMonth) + monthHtml += '' + monthNames[drawMonth] + ''; + else { + var inMinYear = (minDate && minDate.getFullYear() == drawYear); + var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear); + monthHtml += ''; + } + if (!showMonthAfterYear) + html += monthHtml + (secondary || !(changeMonth && changeYear) ? ' ' : ''); + // year selection + if ( !inst.yearshtml ) { + inst.yearshtml = ''; + if (secondary || !changeYear) + html += '' + drawYear + ''; + else { + // determine range of years to display + var years = this._get(inst, 'yearRange').split(':'); + var thisYear = new Date().getFullYear(); + var determineYear = function(value) { + var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) : + (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) : + parseInt(value, 10))); + return (isNaN(year) ? thisYear : year); + }; + var year = determineYear(years[0]); + var endYear = Math.max(year, determineYear(years[1] || '')); + year = (minDate ? Math.max(year, minDate.getFullYear()) : year); + endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); + inst.yearshtml += ''; + //when showing there is no need for later update + if( ! $.browser.mozilla ){ + html += inst.yearshtml; + inst.yearshtml = null; + } else { + // will be replaced later with inst.yearshtml + html += ''; + } + } + } + html += this._get(inst, 'yearSuffix'); + if (showMonthAfterYear) + html += (secondary || !(changeMonth && changeYear) ? ' ' : '') + monthHtml; + html += '
      '; // Close datepicker_header + return html; + }, + + /* Adjust one of the date sub-fields. */ + _adjustInstDate: function(inst, offset, period) { + var year = inst.drawYear + (period == 'Y' ? offset : 0); + var month = inst.drawMonth + (period == 'M' ? offset : 0); + var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + + (period == 'D' ? offset : 0); + var date = this._restrictMinMax(inst, + this._daylightSavingAdjust(new Date(year, month, day))); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + if (period == 'M' || period == 'Y') + this._notifyChange(inst); + }, + + /* Ensure a date is within any min/max bounds. */ + _restrictMinMax: function(inst, date) { + var minDate = this._getMinMaxDate(inst, 'min'); + var maxDate = this._getMinMaxDate(inst, 'max'); + var newDate = (minDate && date < minDate ? minDate : date); + newDate = (maxDate && newDate > maxDate ? maxDate : newDate); + return newDate; + }, + + /* Notify change of month/year. */ + _notifyChange: function(inst) { + var onChange = this._get(inst, 'onChangeMonthYear'); + if (onChange) + onChange.apply((inst.input ? inst.input[0] : null), + [inst.selectedYear, inst.selectedMonth + 1, inst]); + }, + + /* Determine the number of months to show. */ + _getNumberOfMonths: function(inst) { + var numMonths = this._get(inst, 'numberOfMonths'); + return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths)); + }, + + /* Determine the current maximum date - ensure no time components are set. */ + _getMinMaxDate: function(inst, minMax) { + return this._determineDate(inst, this._get(inst, minMax + 'Date'), null); + }, + + /* Find the number of days in a given month. */ + _getDaysInMonth: function(year, month) { + return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); + }, + + /* Find the day of the week of the first of a month. */ + _getFirstDayOfMonth: function(year, month) { + return new Date(year, month, 1).getDay(); + }, + + /* Determines if we should allow a "next/prev" month display change. */ + _canAdjustMonth: function(inst, offset, curYear, curMonth) { + var numMonths = this._getNumberOfMonths(inst); + var date = this._daylightSavingAdjust(new Date(curYear, + curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); + if (offset < 0) + date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); + return this._isInRange(inst, date); + }, + + /* Is the given date in the accepted range? */ + _isInRange: function(inst, date) { + var minDate = this._getMinMaxDate(inst, 'min'); + var maxDate = this._getMinMaxDate(inst, 'max'); + return ((!minDate || date.getTime() >= minDate.getTime()) && + (!maxDate || date.getTime() <= maxDate.getTime())); + }, + + /* Provide the configuration settings for formatting/parsing. */ + _getFormatConfig: function(inst) { + var shortYearCutoff = this._get(inst, 'shortYearCutoff'); + shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff : + new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); + return {shortYearCutoff: shortYearCutoff, + dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'), + monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')}; + }, + + /* Format the given date for display. */ + _formatDate: function(inst, day, month, year) { + if (!day) { + inst.currentDay = inst.selectedDay; + inst.currentMonth = inst.selectedMonth; + inst.currentYear = inst.selectedYear; + } + var date = (day ? (typeof day == 'object' ? day : + this._daylightSavingAdjust(new Date(year, month, day))) : + this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); + return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst)); + } +}); + +/* jQuery extend now ignores nulls! */ +function extendRemove(target, props) { + $.extend(target, props); + for (var name in props) + if (props[name] == null || props[name] == undefined) + target[name] = props[name]; + return target; +}; + +/* Determine whether an object is an array. */ +function isArray(a) { + return (a && (($.browser.safari && typeof a == 'object' && a.length) || + (a.constructor && a.constructor.toString().match(/\Array\(\)/)))); +}; + +/* Invoke the datepicker functionality. + @param options string - a command, optionally followed by additional parameters or + Object - settings for attaching new datepicker functionality + @return jQuery object */ +$.fn.datepicker = function(options){ + + /* Verify an empty collection wasn't passed - Fixes #6976 */ + if ( !this.length ) { + return this; + } + + /* Initialise the date picker. */ + if (!$.datepicker.initialized) { + $(document).mousedown($.datepicker._checkExternalClick). + find('body').append($.datepicker.dpDiv); + $.datepicker.initialized = true; + } + + var otherArgs = Array.prototype.slice.call(arguments, 1); + if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget')) + return $.datepicker['_' + options + 'Datepicker']. + apply($.datepicker, [this[0]].concat(otherArgs)); + if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') + return $.datepicker['_' + options + 'Datepicker']. + apply($.datepicker, [this[0]].concat(otherArgs)); + return this.each(function() { + typeof options == 'string' ? + $.datepicker['_' + options + 'Datepicker']. + apply($.datepicker, [this].concat(otherArgs)) : + $.datepicker._attachDatepicker(this, options); + }); +}; + +$.datepicker = new Datepicker(); // singleton instance +$.datepicker.initialized = false; +$.datepicker.uuid = new Date().getTime(); +$.datepicker.version = "1.8.12"; + +// Workaround for #4055 +// Add another global to avoid noConflict issues with inline event handlers +window['DP_jQuery_' + dpuuid] = $; + +})(jQuery); +/* + * jQuery UI Dialog 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Dialog + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.button.js + * jquery.ui.draggable.js + * jquery.ui.mouse.js + * jquery.ui.position.js + * jquery.ui.resizable.js + */ +(function( $, undefined ) { + +var uiDialogClasses = + 'ui-dialog ' + + 'ui-widget ' + + 'ui-widget-content ' + + 'ui-corner-all ', + sizeRelatedOptions = { + buttons: true, + height: true, + maxHeight: true, + maxWidth: true, + minHeight: true, + minWidth: true, + width: true + }, + resizableRelatedOptions = { + maxHeight: true, + maxWidth: true, + minHeight: true, + minWidth: true + }, + // support for jQuery 1.3.2 - handle common attrFn methods for dialog + attrFn = $.attrFn || { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true, + click: true + }; + +$.widget("ui.dialog", { + options: { + autoOpen: true, + buttons: {}, + closeOnEscape: true, + closeText: 'close', + dialogClass: '', + draggable: true, + hide: null, + height: 'auto', + maxHeight: false, + maxWidth: false, + minHeight: 150, + minWidth: 150, + modal: false, + position: { + my: 'center', + at: 'center', + collision: 'fit', + // ensure that the titlebar is never outside the document + using: function(pos) { + var topOffset = $(this).css(pos).offset().top; + if (topOffset < 0) { + $(this).css('top', pos.top - topOffset); + } + } + }, + resizable: true, + show: null, + stack: true, + title: '', + width: 300, + zIndex: 1000 + }, + + _create: function() { + this.originalTitle = this.element.attr('title'); + // #5742 - .attr() might return a DOMElement + if ( typeof this.originalTitle !== "string" ) { + this.originalTitle = ""; + } + + this.options.title = this.options.title || this.originalTitle; + var self = this, + options = self.options, + + title = options.title || ' ', + titleId = $.ui.dialog.getTitleId(self.element), + + uiDialog = (self.uiDialog = $('
      ')) + .appendTo(document.body) + .hide() + .addClass(uiDialogClasses + options.dialogClass) + .css({ + zIndex: options.zIndex + }) + // setting tabIndex makes the div focusable + // setting outline to 0 prevents a border on focus in Mozilla + .attr('tabIndex', -1).css('outline', 0).keydown(function(event) { + if (options.closeOnEscape && event.keyCode && + event.keyCode === $.ui.keyCode.ESCAPE) { + + self.close(event); + event.preventDefault(); + } + }) + .attr({ + role: 'dialog', + 'aria-labelledby': titleId + }) + .mousedown(function(event) { + self.moveToTop(false, event); + }), + + uiDialogContent = self.element + .show() + .removeAttr('title') + .addClass( + 'ui-dialog-content ' + + 'ui-widget-content') + .appendTo(uiDialog), + + uiDialogTitlebar = (self.uiDialogTitlebar = $('
      ')) + .addClass( + 'ui-dialog-titlebar ' + + 'ui-widget-header ' + + 'ui-corner-all ' + + 'ui-helper-clearfix' + ) + .prependTo(uiDialog), + + uiDialogTitlebarClose = $('') + .addClass( + 'ui-dialog-titlebar-close ' + + 'ui-corner-all' + ) + .attr('role', 'button') + .hover( + function() { + uiDialogTitlebarClose.addClass('ui-state-hover'); + }, + function() { + uiDialogTitlebarClose.removeClass('ui-state-hover'); + } + ) + .focus(function() { + uiDialogTitlebarClose.addClass('ui-state-focus'); + }) + .blur(function() { + uiDialogTitlebarClose.removeClass('ui-state-focus'); + }) + .click(function(event) { + self.close(event); + return false; + }) + .appendTo(uiDialogTitlebar), + + uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('')) + .addClass( + 'ui-icon ' + + 'ui-icon-closethick' + ) + .text(options.closeText) + .appendTo(uiDialogTitlebarClose), + + uiDialogTitle = $('') + .addClass('ui-dialog-title') + .attr('id', titleId) + .html(title) + .prependTo(uiDialogTitlebar); + + //handling of deprecated beforeclose (vs beforeClose) option + //Ticket #4669 http://dev.jqueryui.com/ticket/4669 + //TODO: remove in 1.9pre + if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) { + options.beforeClose = options.beforeclose; + } + + uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection(); + + if (options.draggable && $.fn.draggable) { + self._makeDraggable(); + } + if (options.resizable && $.fn.resizable) { + self._makeResizable(); + } + + self._createButtons(options.buttons); + self._isOpen = false; + + if ($.fn.bgiframe) { + uiDialog.bgiframe(); + } + }, + + _init: function() { + if ( this.options.autoOpen ) { + this.open(); + } + }, + + destroy: function() { + var self = this; + + if (self.overlay) { + self.overlay.destroy(); + } + self.uiDialog.hide(); + self.element + .unbind('.dialog') + .removeData('dialog') + .removeClass('ui-dialog-content ui-widget-content') + .hide().appendTo('body'); + self.uiDialog.remove(); + + if (self.originalTitle) { + self.element.attr('title', self.originalTitle); + } + + return self; + }, + + widget: function() { + return this.uiDialog; + }, + + close: function(event) { + var self = this, + maxZ, thisZ; + + if (false === self._trigger('beforeClose', event)) { + return; + } + + if (self.overlay) { + self.overlay.destroy(); + } + self.uiDialog.unbind('keypress.ui-dialog'); + + self._isOpen = false; + + if (self.options.hide) { + self.uiDialog.hide(self.options.hide, function() { + self._trigger('close', event); + }); + } else { + self.uiDialog.hide(); + self._trigger('close', event); + } + + $.ui.dialog.overlay.resize(); + + // adjust the maxZ to allow other modal dialogs to continue to work (see #4309) + if (self.options.modal) { + maxZ = 0; + $('.ui-dialog').each(function() { + if (this !== self.uiDialog[0]) { + thisZ = $(this).css('z-index'); + if(!isNaN(thisZ)) { + maxZ = Math.max(maxZ, thisZ); + } + } + }); + $.ui.dialog.maxZ = maxZ; + } + + return self; + }, + + isOpen: function() { + return this._isOpen; + }, + + // the force parameter allows us to move modal dialogs to their correct + // position on open + moveToTop: function(force, event) { + var self = this, + options = self.options, + saveScroll; + + if ((options.modal && !force) || + (!options.stack && !options.modal)) { + return self._trigger('focus', event); + } + + if (options.zIndex > $.ui.dialog.maxZ) { + $.ui.dialog.maxZ = options.zIndex; + } + if (self.overlay) { + $.ui.dialog.maxZ += 1; + self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ); + } + + //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed. + // http://ui.jquery.com/bugs/ticket/3193 + saveScroll = { scrollTop: self.element.attr('scrollTop'), scrollLeft: self.element.attr('scrollLeft') }; + $.ui.dialog.maxZ += 1; + self.uiDialog.css('z-index', $.ui.dialog.maxZ); + self.element.attr(saveScroll); + self._trigger('focus', event); + + return self; + }, + + open: function() { + if (this._isOpen) { return; } + + var self = this, + options = self.options, + uiDialog = self.uiDialog; + + self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null; + self._size(); + self._position(options.position); + uiDialog.show(options.show); + self.moveToTop(true); + + // prevent tabbing out of modal dialogs + if (options.modal) { + uiDialog.bind('keypress.ui-dialog', function(event) { + if (event.keyCode !== $.ui.keyCode.TAB) { + return; + } + + var tabbables = $(':tabbable', this), + first = tabbables.filter(':first'), + last = tabbables.filter(':last'); + + if (event.target === last[0] && !event.shiftKey) { + first.focus(1); + return false; + } else if (event.target === first[0] && event.shiftKey) { + last.focus(1); + return false; + } + }); + } + + // set focus to the first tabbable element in the content area or the first button + // if there are no tabbable elements, set focus on the dialog itself + $(self.element.find(':tabbable').get().concat( + uiDialog.find('.ui-dialog-buttonpane :tabbable').get().concat( + uiDialog.get()))).eq(0).focus(); + + self._isOpen = true; + self._trigger('open'); + + return self; + }, + + _createButtons: function(buttons) { + var self = this, + hasButtons = false, + uiDialogButtonPane = $('
      ') + .addClass( + 'ui-dialog-buttonpane ' + + 'ui-widget-content ' + + 'ui-helper-clearfix' + ), + uiButtonSet = $( "
      " ) + .addClass( "ui-dialog-buttonset" ) + .appendTo( uiDialogButtonPane ); + + // if we already have a button pane, remove it + self.uiDialog.find('.ui-dialog-buttonpane').remove(); + + if (typeof buttons === 'object' && buttons !== null) { + $.each(buttons, function() { + return !(hasButtons = true); + }); + } + if (hasButtons) { + $.each(buttons, function(name, props) { + props = $.isFunction( props ) ? + { click: props, text: name } : + props; + var button = $('') + .click(function() { + props.click.apply(self.element[0], arguments); + }) + .appendTo(uiButtonSet); + // can't use .attr( props, true ) with jQuery 1.3.2. + $.each( props, function( key, value ) { + if ( key === "click" ) { + return; + } + if ( key in attrFn ) { + button[ key ]( value ); + } else { + button.attr( key, value ); + } + }); + if ($.fn.button) { + button.button(); + } + }); + uiDialogButtonPane.appendTo(self.uiDialog); + } + }, + + _makeDraggable: function() { + var self = this, + options = self.options, + doc = $(document), + heightBeforeDrag; + + function filteredUi(ui) { + return { + position: ui.position, + offset: ui.offset + }; + } + + self.uiDialog.draggable({ + cancel: '.ui-dialog-content, .ui-dialog-titlebar-close', + handle: '.ui-dialog-titlebar', + containment: 'document', + start: function(event, ui) { + heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height(); + $(this).height($(this).height()).addClass("ui-dialog-dragging"); + self._trigger('dragStart', event, filteredUi(ui)); + }, + drag: function(event, ui) { + self._trigger('drag', event, filteredUi(ui)); + }, + stop: function(event, ui) { + options.position = [ui.position.left - doc.scrollLeft(), + ui.position.top - doc.scrollTop()]; + $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag); + self._trigger('dragStop', event, filteredUi(ui)); + $.ui.dialog.overlay.resize(); + } + }); + }, + + _makeResizable: function(handles) { + handles = (handles === undefined ? this.options.resizable : handles); + var self = this, + options = self.options, + // .ui-resizable has position: relative defined in the stylesheet + // but dialogs have to use absolute or fixed positioning + position = self.uiDialog.css('position'), + resizeHandles = (typeof handles === 'string' ? + handles : + 'n,e,s,w,se,sw,ne,nw' + ); + + function filteredUi(ui) { + return { + originalPosition: ui.originalPosition, + originalSize: ui.originalSize, + position: ui.position, + size: ui.size + }; + } + + self.uiDialog.resizable({ + cancel: '.ui-dialog-content', + containment: 'document', + alsoResize: self.element, + maxWidth: options.maxWidth, + maxHeight: options.maxHeight, + minWidth: options.minWidth, + minHeight: self._minHeight(), + handles: resizeHandles, + start: function(event, ui) { + $(this).addClass("ui-dialog-resizing"); + self._trigger('resizeStart', event, filteredUi(ui)); + }, + resize: function(event, ui) { + self._trigger('resize', event, filteredUi(ui)); + }, + stop: function(event, ui) { + $(this).removeClass("ui-dialog-resizing"); + options.height = $(this).height(); + options.width = $(this).width(); + self._trigger('resizeStop', event, filteredUi(ui)); + $.ui.dialog.overlay.resize(); + } + }) + .css('position', position) + .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se'); + }, + + _minHeight: function() { + var options = this.options; + + if (options.height === 'auto') { + return options.minHeight; + } else { + return Math.min(options.minHeight, options.height); + } + }, + + _position: function(position) { + var myAt = [], + offset = [0, 0], + isVisible; + + if (position) { + // deep extending converts arrays to objects in jQuery <= 1.3.2 :-( + // if (typeof position == 'string' || $.isArray(position)) { + // myAt = $.isArray(position) ? position : position.split(' '); + + if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) { + myAt = position.split ? position.split(' ') : [position[0], position[1]]; + if (myAt.length === 1) { + myAt[1] = myAt[0]; + } + + $.each(['left', 'top'], function(i, offsetPosition) { + if (+myAt[i] === myAt[i]) { + offset[i] = myAt[i]; + myAt[i] = offsetPosition; + } + }); + + position = { + my: myAt.join(" "), + at: myAt.join(" "), + offset: offset.join(" ") + }; + } + + position = $.extend({}, $.ui.dialog.prototype.options.position, position); + } else { + position = $.ui.dialog.prototype.options.position; + } + + // need to show the dialog to get the actual offset in the position plugin + isVisible = this.uiDialog.is(':visible'); + if (!isVisible) { + this.uiDialog.show(); + } + this.uiDialog + // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 + .css({ top: 0, left: 0 }) + .position($.extend({ of: window }, position)); + if (!isVisible) { + this.uiDialog.hide(); + } + }, + + _setOptions: function( options ) { + var self = this, + resizableOptions = {}, + resize = false; + + $.each( options, function( key, value ) { + self._setOption( key, value ); + + if ( key in sizeRelatedOptions ) { + resize = true; + } + if ( key in resizableRelatedOptions ) { + resizableOptions[ key ] = value; + } + }); + + if ( resize ) { + this._size(); + } + if ( this.uiDialog.is( ":data(resizable)" ) ) { + this.uiDialog.resizable( "option", resizableOptions ); + } + }, + + _setOption: function(key, value){ + var self = this, + uiDialog = self.uiDialog; + + switch (key) { + //handling of deprecated beforeclose (vs beforeClose) option + //Ticket #4669 http://dev.jqueryui.com/ticket/4669 + //TODO: remove in 1.9pre + case "beforeclose": + key = "beforeClose"; + break; + case "buttons": + self._createButtons(value); + break; + case "closeText": + // ensure that we always pass a string + self.uiDialogTitlebarCloseText.text("" + value); + break; + case "dialogClass": + uiDialog + .removeClass(self.options.dialogClass) + .addClass(uiDialogClasses + value); + break; + case "disabled": + if (value) { + uiDialog.addClass('ui-dialog-disabled'); + } else { + uiDialog.removeClass('ui-dialog-disabled'); + } + break; + case "draggable": + var isDraggable = uiDialog.is( ":data(draggable)" ); + if ( isDraggable && !value ) { + uiDialog.draggable( "destroy" ); + } + + if ( !isDraggable && value ) { + self._makeDraggable(); + } + break; + case "position": + self._position(value); + break; + case "resizable": + // currently resizable, becoming non-resizable + var isResizable = uiDialog.is( ":data(resizable)" ); + if (isResizable && !value) { + uiDialog.resizable('destroy'); + } + + // currently resizable, changing handles + if (isResizable && typeof value === 'string') { + uiDialog.resizable('option', 'handles', value); + } + + // currently non-resizable, becoming resizable + if (!isResizable && value !== false) { + self._makeResizable(value); + } + break; + case "title": + // convert whatever was passed in o a string, for html() to not throw up + $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || ' ')); + break; + } + + $.Widget.prototype._setOption.apply(self, arguments); + }, + + _size: function() { + /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content + * divs will both have width and height set, so we need to reset them + */ + var options = this.options, + nonContentHeight, + minContentHeight, + isVisible = this.uiDialog.is( ":visible" ); + + // reset content sizing + this.element.show().css({ + width: 'auto', + minHeight: 0, + height: 0 + }); + + if (options.minWidth > options.width) { + options.width = options.minWidth; + } + + // reset wrapper sizing + // determine the height of all the non-content elements + nonContentHeight = this.uiDialog.css({ + height: 'auto', + width: options.width + }) + .height(); + minContentHeight = Math.max( 0, options.minHeight - nonContentHeight ); + + if ( options.height === "auto" ) { + // only needed for IE6 support + if ( $.support.minHeight ) { + this.element.css({ + minHeight: minContentHeight, + height: "auto" + }); + } else { + this.uiDialog.show(); + var autoHeight = this.element.css( "height", "auto" ).height(); + if ( !isVisible ) { + this.uiDialog.hide(); + } + this.element.height( Math.max( autoHeight, minContentHeight ) ); + } + } else { + this.element.height( Math.max( options.height - nonContentHeight, 0 ) ); + } + + if (this.uiDialog.is(':data(resizable)')) { + this.uiDialog.resizable('option', 'minHeight', this._minHeight()); + } + } +}); + +$.extend($.ui.dialog, { + version: "1.8.12", + + uuid: 0, + maxZ: 0, + + getTitleId: function($el) { + var id = $el.attr('id'); + if (!id) { + this.uuid += 1; + id = this.uuid; + } + return 'ui-dialog-title-' + id; + }, + + overlay: function(dialog) { + this.$el = $.ui.dialog.overlay.create(dialog); + } +}); + +$.extend($.ui.dialog.overlay, { + instances: [], + // reuse old instances due to IE memory leak with alpha transparency (see #5185) + oldInstances: [], + maxZ: 0, + events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','), + function(event) { return event + '.dialog-overlay'; }).join(' '), + create: function(dialog) { + if (this.instances.length === 0) { + // prevent use of anchors and inputs + // we use a setTimeout in case the overlay is created from an + // event that we're going to be cancelling (see #2804) + setTimeout(function() { + // handle $(el).dialog().dialog('close') (see #4065) + if ($.ui.dialog.overlay.instances.length) { + $(document).bind($.ui.dialog.overlay.events, function(event) { + // stop events if the z-index of the target is < the z-index of the overlay + // we cannot return true when we don't want to cancel the event (#3523) + if ($(event.target).zIndex() < $.ui.dialog.overlay.maxZ) { + return false; + } + }); + } + }, 1); + + // allow closing by pressing the escape key + $(document).bind('keydown.dialog-overlay', function(event) { + if (dialog.options.closeOnEscape && event.keyCode && + event.keyCode === $.ui.keyCode.ESCAPE) { + + dialog.close(event); + event.preventDefault(); + } + }); + + // handle window resize + $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize); + } + + var $el = (this.oldInstances.pop() || $('
      ').addClass('ui-widget-overlay')) + .appendTo(document.body) + .css({ + width: this.width(), + height: this.height() + }); + + if ($.fn.bgiframe) { + $el.bgiframe(); + } + + this.instances.push($el); + return $el; + }, + + destroy: function($el) { + var indexOf = $.inArray($el, this.instances); + if (indexOf != -1){ + this.oldInstances.push(this.instances.splice(indexOf, 1)[0]); + } + + if (this.instances.length === 0) { + $([document, window]).unbind('.dialog-overlay'); + } + + $el.remove(); + + // adjust the maxZ to allow other modal dialogs to continue to work (see #4309) + var maxZ = 0; + $.each(this.instances, function() { + maxZ = Math.max(maxZ, this.css('z-index')); + }); + this.maxZ = maxZ; + }, + + height: function() { + var scrollHeight, + offsetHeight; + // handle IE 6 + if ($.browser.msie && $.browser.version < 7) { + scrollHeight = Math.max( + document.documentElement.scrollHeight, + document.body.scrollHeight + ); + offsetHeight = Math.max( + document.documentElement.offsetHeight, + document.body.offsetHeight + ); + + if (scrollHeight < offsetHeight) { + return $(window).height() + 'px'; + } else { + return scrollHeight + 'px'; + } + // handle "good" browsers + } else { + return $(document).height() + 'px'; + } + }, + + width: function() { + var scrollWidth, + offsetWidth; + // handle IE 6 + if ($.browser.msie && $.browser.version < 7) { + scrollWidth = Math.max( + document.documentElement.scrollWidth, + document.body.scrollWidth + ); + offsetWidth = Math.max( + document.documentElement.offsetWidth, + document.body.offsetWidth + ); + + if (scrollWidth < offsetWidth) { + return $(window).width() + 'px'; + } else { + return scrollWidth + 'px'; + } + // handle "good" browsers + } else { + return $(document).width() + 'px'; + } + }, + + resize: function() { + /* If the dialog is draggable and the user drags it past the + * right edge of the window, the document becomes wider so we + * need to stretch the overlay. If the user then drags the + * dialog back to the left, the document will become narrower, + * so we need to shrink the overlay to the appropriate size. + * This is handled by shrinking the overlay before setting it + * to the full document size. + */ + var $overlays = $([]); + $.each($.ui.dialog.overlay.instances, function() { + $overlays = $overlays.add(this); + }); + + $overlays.css({ + width: 0, + height: 0 + }).css({ + width: $.ui.dialog.overlay.width(), + height: $.ui.dialog.overlay.height() + }); + } +}); + +$.extend($.ui.dialog.overlay.prototype, { + destroy: function() { + $.ui.dialog.overlay.destroy(this.$el); + } +}); + +}(jQuery)); +/* + * jQuery UI Position 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Position + */ +(function( $, undefined ) { + +$.ui = $.ui || {}; + +var horizontalPositions = /left|center|right/, + verticalPositions = /top|center|bottom/, + center = "center", + _position = $.fn.position, + _offset = $.fn.offset; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var target = $( options.of ), + targetElem = target[0], + collision = ( options.collision || "flip" ).split( " " ), + offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ], + targetWidth, + targetHeight, + basePosition; + + if ( targetElem.nodeType === 9 ) { + targetWidth = target.width(); + targetHeight = target.height(); + basePosition = { top: 0, left: 0 }; + // TODO: use $.isWindow() in 1.9 + } else if ( targetElem.setTimeout ) { + targetWidth = target.width(); + targetHeight = target.height(); + basePosition = { top: target.scrollTop(), left: target.scrollLeft() }; + } else if ( targetElem.preventDefault ) { + // force left top to allow flipping + options.at = "left top"; + targetWidth = targetHeight = 0; + basePosition = { top: options.of.pageY, left: options.of.pageX }; + } else { + targetWidth = target.outerWidth(); + targetHeight = target.outerHeight(); + basePosition = target.offset(); + } + + // force my and at to have valid horizontal and veritcal positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[this] || "" ).split( " " ); + if ( pos.length === 1) { + pos = horizontalPositions.test( pos[0] ) ? + pos.concat( [center] ) : + verticalPositions.test( pos[0] ) ? + [ center ].concat( pos ) : + [ center, center ]; + } + pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center; + pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center; + options[ this ] = pos; + }); + + // normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + // normalize offset option + offset[ 0 ] = parseInt( offset[0], 10 ) || 0; + if ( offset.length === 1 ) { + offset[ 1 ] = offset[ 0 ]; + } + offset[ 1 ] = parseInt( offset[1], 10 ) || 0; + + if ( options.at[0] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[0] === center ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[1] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[1] === center ) { + basePosition.top += targetHeight / 2; + } + + basePosition.left += offset[ 0 ]; + basePosition.top += offset[ 1 ]; + + return this.each(function() { + var elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0, + marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0, + collisionWidth = elemWidth + marginLeft + + ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ), + collisionHeight = elemHeight + marginTop + + ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ), + position = $.extend( {}, basePosition ), + collisionPosition; + + if ( options.my[0] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[0] === center ) { + position.left -= elemWidth / 2; + } + + if ( options.my[1] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[1] === center ) { + position.top -= elemHeight / 2; + } + + // prevent fractions (see #5280) + position.left = Math.round( position.left ); + position.top = Math.round( position.top ); + + collisionPosition = { + left: position.left - marginLeft, + top: position.top - marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[i] ] ) { + $.ui.position[ collision[i] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: offset, + my: options.my, + at: options.at + }); + } + }); + + if ( $.fn.bgiframe ) { + elem.bgiframe(); + } + elem.offset( $.extend( position, { using: options.using } ) ); + }); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var win = $( window ), + over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(); + position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left ); + }, + top: function( position, data ) { + var win = $( window ), + over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(); + position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top ); + } + }, + + flip: { + left: function( position, data ) { + if ( data.at[0] === center ) { + return; + } + var win = $( window ), + over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(), + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + -data.targetWidth, + offset = -2 * data.offset[ 0 ]; + position.left += data.collisionPosition.left < 0 ? + myOffset + atOffset + offset : + over > 0 ? + myOffset + atOffset + offset : + 0; + }, + top: function( position, data ) { + if ( data.at[1] === center ) { + return; + } + var win = $( window ), + over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(), + myOffset = data.my[ 1 ] === "top" ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + -data.targetHeight, + offset = -2 * data.offset[ 1 ]; + position.top += data.collisionPosition.top < 0 ? + myOffset + atOffset + offset : + over > 0 ? + myOffset + atOffset + offset : + 0; + } + } +}; + +// offset setter from jQuery 1.4 +if ( !$.offset.setOffset ) { + $.offset.setOffset = function( elem, options ) { + // set position first, in-case top/left are set even on static elem + if ( /static/.test( $.curCSS( elem, "position" ) ) ) { + elem.style.position = "relative"; + } + var curElem = $( elem ), + curOffset = curElem.offset(), + curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0, + curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0, + props = { + top: (options.top - curOffset.top) + curTop, + left: (options.left - curOffset.left) + curLeft + }; + + if ( 'using' in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + }; + + $.fn.offset = function( options ) { + var elem = this[ 0 ]; + if ( !elem || !elem.ownerDocument ) { return null; } + if ( options ) { + return this.each(function() { + $.offset.setOffset( this, options ); + }); + } + return _offset.call( this ); + }; +} + +}( jQuery )); +/* + * jQuery UI Progressbar 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +$.widget( "ui.progressbar", { + options: { + value: 0, + max: 100 + }, + + min: 0, + + _create: function() { + this.element + .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) + .attr({ + role: "progressbar", + "aria-valuemin": this.min, + "aria-valuemax": this.options.max, + "aria-valuenow": this._value() + }); + + this.valueDiv = $( "
      " ) + .appendTo( this.element ); + + this.oldValue = this._value(); + this._refreshValue(); + }, + + destroy: function() { + this.element + .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) + .removeAttr( "role" ) + .removeAttr( "aria-valuemin" ) + .removeAttr( "aria-valuemax" ) + .removeAttr( "aria-valuenow" ); + + this.valueDiv.remove(); + + $.Widget.prototype.destroy.apply( this, arguments ); + }, + + value: function( newValue ) { + if ( newValue === undefined ) { + return this._value(); + } + + this._setOption( "value", newValue ); + return this; + }, + + _setOption: function( key, value ) { + if ( key === "value" ) { + this.options.value = value; + this._refreshValue(); + if ( this._value() === this.options.max ) { + this._trigger( "complete" ); + } + } + + $.Widget.prototype._setOption.apply( this, arguments ); + }, + + _value: function() { + var val = this.options.value; + // normalize invalid value + if ( typeof val !== "number" ) { + val = 0; + } + return Math.min( this.options.max, Math.max( this.min, val ) ); + }, + + _percentage: function() { + return 100 * this._value() / this.options.max; + }, + + _refreshValue: function() { + var value = this.value(); + var percentage = this._percentage(); + + if ( this.oldValue !== value ) { + this.oldValue = value; + this._trigger( "change" ); + } + + this.valueDiv + .toggle( value > this.min ) + .toggleClass( "ui-corner-right", value === this.options.max ) + .width( percentage.toFixed(0) + "%" ); + this.element.attr( "aria-valuenow", value ); + } +}); + +$.extend( $.ui.progressbar, { + version: "1.8.12" +}); + +})( jQuery ); +/* + * jQuery UI Slider 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +// number of pages in a slider +// (how many times can you page up/down to go through the whole range) +var numPages = 5; + +$.widget( "ui.slider", $.ui.mouse, { + + widgetEventPrefix: "slide", + + options: { + animate: false, + distance: 0, + max: 100, + min: 0, + orientation: "horizontal", + range: false, + step: 1, + value: 0, + values: null + }, + + _create: function() { + var self = this, + o = this.options; + + this._keySliding = false; + this._mouseSliding = false; + this._animateOff = true; + this._handleIndex = null; + this._detectOrientation(); + this._mouseInit(); + + this.element + .addClass( "ui-slider" + + " ui-slider-" + this.orientation + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all" ); + + if ( o.disabled ) { + this.element.addClass( "ui-slider-disabled ui-disabled" ); + } + + this.range = $([]); + + if ( o.range ) { + if ( o.range === true ) { + this.range = $( "
      " ); + if ( !o.values ) { + o.values = [ this._valueMin(), this._valueMin() ]; + } + if ( o.values.length && o.values.length !== 2 ) { + o.values = [ o.values[0], o.values[0] ]; + } + } else { + this.range = $( "
      " ); + } + + this.range + .appendTo( this.element ) + .addClass( "ui-slider-range" ); + + if ( o.range === "min" || o.range === "max" ) { + this.range.addClass( "ui-slider-range-" + o.range ); + } + + // note: this isn't the most fittingly semantic framework class for this element, + // but worked best visually with a variety of themes + this.range.addClass( "ui-widget-header" ); + } + + if ( $( ".ui-slider-handle", this.element ).length === 0 ) { + $( "" ) + .appendTo( this.element ) + .addClass( "ui-slider-handle" ); + } + + if ( o.values && o.values.length ) { + while ( $(".ui-slider-handle", this.element).length < o.values.length ) { + $( "" ) + .appendTo( this.element ) + .addClass( "ui-slider-handle" ); + } + } + + this.handles = $( ".ui-slider-handle", this.element ) + .addClass( "ui-state-default" + + " ui-corner-all" ); + + this.handle = this.handles.eq( 0 ); + + this.handles.add( this.range ).filter( "a" ) + .click(function( event ) { + event.preventDefault(); + }) + .hover(function() { + if ( !o.disabled ) { + $( this ).addClass( "ui-state-hover" ); + } + }, function() { + $( this ).removeClass( "ui-state-hover" ); + }) + .focus(function() { + if ( !o.disabled ) { + $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" ); + $( this ).addClass( "ui-state-focus" ); + } else { + $( this ).blur(); + } + }) + .blur(function() { + $( this ).removeClass( "ui-state-focus" ); + }); + + this.handles.each(function( i ) { + $( this ).data( "index.ui-slider-handle", i ); + }); + + this.handles + .keydown(function( event ) { + var ret = true, + index = $( this ).data( "index.ui-slider-handle" ), + allowed, + curVal, + newVal, + step; + + if ( self.options.disabled ) { + return; + } + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + case $.ui.keyCode.END: + case $.ui.keyCode.PAGE_UP: + case $.ui.keyCode.PAGE_DOWN: + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + ret = false; + if ( !self._keySliding ) { + self._keySliding = true; + $( this ).addClass( "ui-state-active" ); + allowed = self._start( event, index ); + if ( allowed === false ) { + return; + } + } + break; + } + + step = self.options.step; + if ( self.options.values && self.options.values.length ) { + curVal = newVal = self.values( index ); + } else { + curVal = newVal = self.value(); + } + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + newVal = self._valueMin(); + break; + case $.ui.keyCode.END: + newVal = self._valueMax(); + break; + case $.ui.keyCode.PAGE_UP: + newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) ); + break; + case $.ui.keyCode.PAGE_DOWN: + newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) ); + break; + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + if ( curVal === self._valueMax() ) { + return; + } + newVal = self._trimAlignValue( curVal + step ); + break; + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + if ( curVal === self._valueMin() ) { + return; + } + newVal = self._trimAlignValue( curVal - step ); + break; + } + + self._slide( event, index, newVal ); + + return ret; + + }) + .keyup(function( event ) { + var index = $( this ).data( "index.ui-slider-handle" ); + + if ( self._keySliding ) { + self._keySliding = false; + self._stop( event, index ); + self._change( event, index ); + $( this ).removeClass( "ui-state-active" ); + } + + }); + + this._refreshValue(); + + this._animateOff = false; + }, + + destroy: function() { + this.handles.remove(); + this.range.remove(); + + this.element + .removeClass( "ui-slider" + + " ui-slider-horizontal" + + " ui-slider-vertical" + + " ui-slider-disabled" + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all" ) + .removeData( "slider" ) + .unbind( ".slider" ); + + this._mouseDestroy(); + + return this; + }, + + _mouseCapture: function( event ) { + var o = this.options, + position, + normValue, + distance, + closestHandle, + self, + index, + allowed, + offset, + mouseOverHandle; + + if ( o.disabled ) { + return false; + } + + this.elementSize = { + width: this.element.outerWidth(), + height: this.element.outerHeight() + }; + this.elementOffset = this.element.offset(); + + position = { x: event.pageX, y: event.pageY }; + normValue = this._normValueFromMouse( position ); + distance = this._valueMax() - this._valueMin() + 1; + self = this; + this.handles.each(function( i ) { + var thisDistance = Math.abs( normValue - self.values(i) ); + if ( distance > thisDistance ) { + distance = thisDistance; + closestHandle = $( this ); + index = i; + } + }); + + // workaround for bug #3736 (if both handles of a range are at 0, + // the first is always used as the one with least distance, + // and moving it is obviously prevented by preventing negative ranges) + if( o.range === true && this.values(1) === o.min ) { + index += 1; + closestHandle = $( this.handles[index] ); + } + + allowed = this._start( event, index ); + if ( allowed === false ) { + return false; + } + this._mouseSliding = true; + + self._handleIndex = index; + + closestHandle + .addClass( "ui-state-active" ) + .focus(); + + offset = closestHandle.offset(); + mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" ); + this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { + left: event.pageX - offset.left - ( closestHandle.width() / 2 ), + top: event.pageY - offset.top - + ( closestHandle.height() / 2 ) - + ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - + ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + + ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) + }; + + if ( !this.handles.hasClass( "ui-state-hover" ) ) { + this._slide( event, index, normValue ); + } + this._animateOff = true; + return true; + }, + + _mouseStart: function( event ) { + return true; + }, + + _mouseDrag: function( event ) { + var position = { x: event.pageX, y: event.pageY }, + normValue = this._normValueFromMouse( position ); + + this._slide( event, this._handleIndex, normValue ); + + return false; + }, + + _mouseStop: function( event ) { + this.handles.removeClass( "ui-state-active" ); + this._mouseSliding = false; + + this._stop( event, this._handleIndex ); + this._change( event, this._handleIndex ); + + this._handleIndex = null; + this._clickOffset = null; + this._animateOff = false; + + return false; + }, + + _detectOrientation: function() { + this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; + }, + + _normValueFromMouse: function( position ) { + var pixelTotal, + pixelMouse, + percentMouse, + valueTotal, + valueMouse; + + if ( this.orientation === "horizontal" ) { + pixelTotal = this.elementSize.width; + pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); + } else { + pixelTotal = this.elementSize.height; + pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); + } + + percentMouse = ( pixelMouse / pixelTotal ); + if ( percentMouse > 1 ) { + percentMouse = 1; + } + if ( percentMouse < 0 ) { + percentMouse = 0; + } + if ( this.orientation === "vertical" ) { + percentMouse = 1 - percentMouse; + } + + valueTotal = this._valueMax() - this._valueMin(); + valueMouse = this._valueMin() + percentMouse * valueTotal; + + return this._trimAlignValue( valueMouse ); + }, + + _start: function( event, index ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + return this._trigger( "start", event, uiHash ); + }, + + _slide: function( event, index, newVal ) { + var otherVal, + newValues, + allowed; + + if ( this.options.values && this.options.values.length ) { + otherVal = this.values( index ? 0 : 1 ); + + if ( ( this.options.values.length === 2 && this.options.range === true ) && + ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) + ) { + newVal = otherVal; + } + + if ( newVal !== this.values( index ) ) { + newValues = this.values(); + newValues[ index ] = newVal; + // A slide can be canceled by returning false from the slide callback + allowed = this._trigger( "slide", event, { + handle: this.handles[ index ], + value: newVal, + values: newValues + } ); + otherVal = this.values( index ? 0 : 1 ); + if ( allowed !== false ) { + this.values( index, newVal, true ); + } + } + } else { + if ( newVal !== this.value() ) { + // A slide can be canceled by returning false from the slide callback + allowed = this._trigger( "slide", event, { + handle: this.handles[ index ], + value: newVal + } ); + if ( allowed !== false ) { + this.value( newVal ); + } + } + } + }, + + _stop: function( event, index ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + + this._trigger( "stop", event, uiHash ); + }, + + _change: function( event, index ) { + if ( !this._keySliding && !this._mouseSliding ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + + this._trigger( "change", event, uiHash ); + } + }, + + value: function( newValue ) { + if ( arguments.length ) { + this.options.value = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, 0 ); + return; + } + + return this._value(); + }, + + values: function( index, newValue ) { + var vals, + newValues, + i; + + if ( arguments.length > 1 ) { + this.options.values[ index ] = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, index ); + return; + } + + if ( arguments.length ) { + if ( $.isArray( arguments[ 0 ] ) ) { + vals = this.options.values; + newValues = arguments[ 0 ]; + for ( i = 0; i < vals.length; i += 1 ) { + vals[ i ] = this._trimAlignValue( newValues[ i ] ); + this._change( null, i ); + } + this._refreshValue(); + } else { + if ( this.options.values && this.options.values.length ) { + return this._values( index ); + } else { + return this.value(); + } + } + } else { + return this._values(); + } + }, + + _setOption: function( key, value ) { + var i, + valsLength = 0; + + if ( $.isArray( this.options.values ) ) { + valsLength = this.options.values.length; + } + + $.Widget.prototype._setOption.apply( this, arguments ); + + switch ( key ) { + case "disabled": + if ( value ) { + this.handles.filter( ".ui-state-focus" ).blur(); + this.handles.removeClass( "ui-state-hover" ); + this.handles.attr( "disabled", "disabled" ); + this.element.addClass( "ui-disabled" ); + } else { + this.handles.removeAttr( "disabled" ); + this.element.removeClass( "ui-disabled" ); + } + break; + case "orientation": + this._detectOrientation(); + this.element + .removeClass( "ui-slider-horizontal ui-slider-vertical" ) + .addClass( "ui-slider-" + this.orientation ); + this._refreshValue(); + break; + case "value": + this._animateOff = true; + this._refreshValue(); + this._change( null, 0 ); + this._animateOff = false; + break; + case "values": + this._animateOff = true; + this._refreshValue(); + for ( i = 0; i < valsLength; i += 1 ) { + this._change( null, i ); + } + this._animateOff = false; + break; + } + }, + + //internal value getter + // _value() returns value trimmed by min and max, aligned by step + _value: function() { + var val = this.options.value; + val = this._trimAlignValue( val ); + + return val; + }, + + //internal values getter + // _values() returns array of values trimmed by min and max, aligned by step + // _values( index ) returns single value trimmed by min and max, aligned by step + _values: function( index ) { + var val, + vals, + i; + + if ( arguments.length ) { + val = this.options.values[ index ]; + val = this._trimAlignValue( val ); + + return val; + } else { + // .slice() creates a copy of the array + // this copy gets trimmed by min and max and then returned + vals = this.options.values.slice(); + for ( i = 0; i < vals.length; i+= 1) { + vals[ i ] = this._trimAlignValue( vals[ i ] ); + } + + return vals; + } + }, + + // returns the step-aligned value that val is closest to, between (inclusive) min and max + _trimAlignValue: function( val ) { + if ( val <= this._valueMin() ) { + return this._valueMin(); + } + if ( val >= this._valueMax() ) { + return this._valueMax(); + } + var step = ( this.options.step > 0 ) ? this.options.step : 1, + valModStep = (val - this._valueMin()) % step; + alignValue = val - valModStep; + + if ( Math.abs(valModStep) * 2 >= step ) { + alignValue += ( valModStep > 0 ) ? step : ( -step ); + } + + // Since JavaScript has problems with large floats, round + // the final value to 5 digits after the decimal point (see #4124) + return parseFloat( alignValue.toFixed(5) ); + }, + + _valueMin: function() { + return this.options.min; + }, + + _valueMax: function() { + return this.options.max; + }, + + _refreshValue: function() { + var oRange = this.options.range, + o = this.options, + self = this, + animate = ( !this._animateOff ) ? o.animate : false, + valPercent, + _set = {}, + lastValPercent, + value, + valueMin, + valueMax; + + if ( this.options.values && this.options.values.length ) { + this.handles.each(function( i, j ) { + valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100; + _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + if ( self.options.range === true ) { + if ( self.orientation === "horizontal" ) { + if ( i === 0 ) { + self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); + } + if ( i === 1 ) { + self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } else { + if ( i === 0 ) { + self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); + } + if ( i === 1 ) { + self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } + } + lastValPercent = valPercent; + }); + } else { + value = this.value(); + valueMin = this._valueMin(); + valueMax = this._valueMax(); + valPercent = ( valueMax !== valueMin ) ? + ( value - valueMin ) / ( valueMax - valueMin ) * 100 : + 0; + _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + + if ( oRange === "min" && this.orientation === "horizontal" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); + } + if ( oRange === "max" && this.orientation === "horizontal" ) { + this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + if ( oRange === "min" && this.orientation === "vertical" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); + } + if ( oRange === "max" && this.orientation === "vertical" ) { + this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } + } + +}); + +$.extend( $.ui.slider, { + version: "1.8.12" +}); + +}(jQuery)); +/* + * jQuery UI Tabs 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +var tabId = 0, + listId = 0; + +function getNextTabId() { + return ++tabId; +} + +function getNextListId() { + return ++listId; +} + +$.widget( "ui.tabs", { + options: { + add: null, + ajaxOptions: null, + cache: false, + cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true } + collapsible: false, + disable: null, + disabled: [], + enable: null, + event: "click", + fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 } + idPrefix: "ui-tabs-", + load: null, + panelTemplate: "
      ", + remove: null, + select: null, + show: null, + spinner: "Loading…", + tabTemplate: "
    • #{label}
    • " + }, + + _create: function() { + this._tabify( true ); + }, + + _setOption: function( key, value ) { + if ( key == "selected" ) { + if (this.options.collapsible && value == this.options.selected ) { + return; + } + this.select( value ); + } else { + this.options[ key ] = value; + this._tabify(); + } + }, + + _tabId: function( a ) { + return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) || + this.options.idPrefix + getNextTabId(); + }, + + _sanitizeSelector: function( hash ) { + // we need this because an id may contain a ":" + return hash.replace( /:/g, "\\:" ); + }, + + _cookie: function() { + var cookie = this.cookie || + ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() ); + return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) ); + }, + + _ui: function( tab, panel ) { + return { + tab: tab, + panel: panel, + index: this.anchors.index( tab ) + }; + }, + + _cleanup: function() { + // restore all former loading tabs labels + this.lis.filter( ".ui-state-processing" ) + .removeClass( "ui-state-processing" ) + .find( "span:data(label.tabs)" ) + .each(function() { + var el = $( this ); + el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" ); + }); + }, + + _tabify: function( init ) { + var self = this, + o = this.options, + fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash + + this.list = this.element.find( "ol,ul" ).eq( 0 ); + this.lis = $( " > li:has(a[href])", this.list ); + this.anchors = this.lis.map(function() { + return $( "a", this )[ 0 ]; + }); + this.panels = $( [] ); + + this.anchors.each(function( i, a ) { + var href = $( a ).attr( "href" ); + // For dynamically created HTML that contains a hash as href IE < 8 expands + // such href to the full page url with hash and then misinterprets tab as ajax. + // Same consideration applies for an added tab with a fragment identifier + // since a[href=#fragment-identifier] does unexpectedly not match. + // Thus normalize href attribute... + var hrefBase = href.split( "#" )[ 0 ], + baseEl; + if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] || + ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) { + href = a.hash; + a.href = href; + } + + // inline tab + if ( fragmentId.test( href ) ) { + self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) ); + // remote tab + // prevent loading the page itself if href is just "#" + } else if ( href && href !== "#" ) { + // required for restore on destroy + $.data( a, "href.tabs", href ); + + // TODO until #3808 is fixed strip fragment identifier from url + // (IE fails to load from such url) + $.data( a, "load.tabs", href.replace( /#.*$/, "" ) ); + + var id = self._tabId( a ); + a.href = "#" + id; + var $panel = self.element.find( "#" + id ); + if ( !$panel.length ) { + $panel = $( o.panelTemplate ) + .attr( "id", id ) + .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) + .insertAfter( self.panels[ i - 1 ] || self.list ); + $panel.data( "destroy.tabs", true ); + } + self.panels = self.panels.add( $panel ); + // invalid tab href + } else { + o.disabled.push( i ); + } + }); + + // initialization from scratch + if ( init ) { + // attach necessary classes for styling + this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ); + this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); + this.lis.addClass( "ui-state-default ui-corner-top" ); + this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ); + + // Selected tab + // use "selected" option or try to retrieve: + // 1. from fragment identifier in url + // 2. from cookie + // 3. from selected class attribute on
    • + if ( o.selected === undefined ) { + if ( location.hash ) { + this.anchors.each(function( i, a ) { + if ( a.hash == location.hash ) { + o.selected = i; + return false; + } + }); + } + if ( typeof o.selected !== "number" && o.cookie ) { + o.selected = parseInt( self._cookie(), 10 ); + } + if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) { + o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) ); + } + o.selected = o.selected || ( this.lis.length ? 0 : -1 ); + } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release + o.selected = -1; + } + + // sanity check - default to first tab... + o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 ) + ? o.selected + : 0; + + // Take disabling tabs via class attribute from HTML + // into account and update option properly. + // A selected tab cannot become disabled. + o.disabled = $.unique( o.disabled.concat( + $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) { + return self.lis.index( n ); + }) + ) ).sort(); + + if ( $.inArray( o.selected, o.disabled ) != -1 ) { + o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 ); + } + + // highlight selected tab + this.panels.addClass( "ui-tabs-hide" ); + this.lis.removeClass( "ui-tabs-selected ui-state-active" ); + // check for length avoids error when initializing empty list + if ( o.selected >= 0 && this.anchors.length ) { + self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" ); + this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" ); + + // seems to be expected behavior that the show callback is fired + self.element.queue( "tabs", function() { + self._trigger( "show", null, + self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) ); + }); + + this.load( o.selected ); + } + + // clean up to avoid memory leaks in certain versions of IE 6 + // TODO: namespace this event + $( window ).bind( "unload", function() { + self.lis.add( self.anchors ).unbind( ".tabs" ); + self.lis = self.anchors = self.panels = null; + }); + // update selected after add/remove + } else { + o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) ); + } + + // update collapsible + // TODO: use .toggleClass() + this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" ); + + // set or update cookie after init and add/remove respectively + if ( o.cookie ) { + this._cookie( o.selected, o.cookie ); + } + + // disable tabs + for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) { + $( li )[ $.inArray( i, o.disabled ) != -1 && + // TODO: use .toggleClass() + !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" ); + } + + // reset cache if switching from cached to not cached + if ( o.cache === false ) { + this.anchors.removeData( "cache.tabs" ); + } + + // remove all handlers before, tabify may run on existing tabs after add or option change + this.lis.add( this.anchors ).unbind( ".tabs" ); + + if ( o.event !== "mouseover" ) { + var addState = function( state, el ) { + if ( el.is( ":not(.ui-state-disabled)" ) ) { + el.addClass( "ui-state-" + state ); + } + }; + var removeState = function( state, el ) { + el.removeClass( "ui-state-" + state ); + }; + this.lis.bind( "mouseover.tabs" , function() { + addState( "hover", $( this ) ); + }); + this.lis.bind( "mouseout.tabs", function() { + removeState( "hover", $( this ) ); + }); + this.anchors.bind( "focus.tabs", function() { + addState( "focus", $( this ).closest( "li" ) ); + }); + this.anchors.bind( "blur.tabs", function() { + removeState( "focus", $( this ).closest( "li" ) ); + }); + } + + // set up animations + var hideFx, showFx; + if ( o.fx ) { + if ( $.isArray( o.fx ) ) { + hideFx = o.fx[ 0 ]; + showFx = o.fx[ 1 ]; + } else { + hideFx = showFx = o.fx; + } + } + + // Reset certain styles left over from animation + // and prevent IE's ClearType bug... + function resetStyle( $el, fx ) { + $el.css( "display", "" ); + if ( !$.support.opacity && fx.opacity ) { + $el[ 0 ].style.removeAttribute( "filter" ); + } + } + + // Show a tab... + var showTab = showFx + ? function( clicked, $show ) { + $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" ); + $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way + .animate( showFx, showFx.duration || "normal", function() { + resetStyle( $show, showFx ); + self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) ); + }); + } + : function( clicked, $show ) { + $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" ); + $show.removeClass( "ui-tabs-hide" ); + self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) ); + }; + + // Hide a tab, $show is optional... + var hideTab = hideFx + ? function( clicked, $hide ) { + $hide.animate( hideFx, hideFx.duration || "normal", function() { + self.lis.removeClass( "ui-tabs-selected ui-state-active" ); + $hide.addClass( "ui-tabs-hide" ); + resetStyle( $hide, hideFx ); + self.element.dequeue( "tabs" ); + }); + } + : function( clicked, $hide, $show ) { + self.lis.removeClass( "ui-tabs-selected ui-state-active" ); + $hide.addClass( "ui-tabs-hide" ); + self.element.dequeue( "tabs" ); + }; + + // attach tab event handler, unbind to avoid duplicates from former tabifying... + this.anchors.bind( o.event + ".tabs", function() { + var el = this, + $li = $(el).closest( "li" ), + $hide = self.panels.filter( ":not(.ui-tabs-hide)" ), + $show = self.element.find( self._sanitizeSelector( el.hash ) ); + + // If tab is already selected and not collapsible or tab disabled or + // or is already loading or click callback returns false stop here. + // Check if click handler returns false last so that it is not executed + // for a disabled or loading tab! + if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) || + $li.hasClass( "ui-state-disabled" ) || + $li.hasClass( "ui-state-processing" ) || + self.panels.filter( ":animated" ).length || + self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) { + this.blur(); + return false; + } + + o.selected = self.anchors.index( this ); + + self.abort(); + + // if tab may be closed + if ( o.collapsible ) { + if ( $li.hasClass( "ui-tabs-selected" ) ) { + o.selected = -1; + + if ( o.cookie ) { + self._cookie( o.selected, o.cookie ); + } + + self.element.queue( "tabs", function() { + hideTab( el, $hide ); + }).dequeue( "tabs" ); + + this.blur(); + return false; + } else if ( !$hide.length ) { + if ( o.cookie ) { + self._cookie( o.selected, o.cookie ); + } + + self.element.queue( "tabs", function() { + showTab( el, $show ); + }); + + // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171 + self.load( self.anchors.index( this ) ); + + this.blur(); + return false; + } + } + + if ( o.cookie ) { + self._cookie( o.selected, o.cookie ); + } + + // show new tab + if ( $show.length ) { + if ( $hide.length ) { + self.element.queue( "tabs", function() { + hideTab( el, $hide ); + }); + } + self.element.queue( "tabs", function() { + showTab( el, $show ); + }); + + self.load( self.anchors.index( this ) ); + } else { + throw "jQuery UI Tabs: Mismatching fragment identifier."; + } + + // Prevent IE from keeping other link focussed when using the back button + // and remove dotted border from clicked link. This is controlled via CSS + // in modern browsers; blur() removes focus from address bar in Firefox + // which can become a usability and annoying problem with tabs('rotate'). + if ( $.browser.msie ) { + this.blur(); + } + }); + + // disable click in any case + this.anchors.bind( "click.tabs", function(){ + return false; + }); + }, + + _getIndex: function( index ) { + // meta-function to give users option to provide a href string instead of a numerical index. + // also sanitizes numerical indexes to valid values. + if ( typeof index == "string" ) { + index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) ); + } + + return index; + }, + + destroy: function() { + var o = this.options; + + this.abort(); + + this.element + .unbind( ".tabs" ) + .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ) + .removeData( "tabs" ); + + this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ); + + this.anchors.each(function() { + var href = $.data( this, "href.tabs" ); + if ( href ) { + this.href = href; + } + var $this = $( this ).unbind( ".tabs" ); + $.each( [ "href", "load", "cache" ], function( i, prefix ) { + $this.removeData( prefix + ".tabs" ); + }); + }); + + this.lis.unbind( ".tabs" ).add( this.panels ).each(function() { + if ( $.data( this, "destroy.tabs" ) ) { + $( this ).remove(); + } else { + $( this ).removeClass([ + "ui-state-default", + "ui-corner-top", + "ui-tabs-selected", + "ui-state-active", + "ui-state-hover", + "ui-state-focus", + "ui-state-disabled", + "ui-tabs-panel", + "ui-widget-content", + "ui-corner-bottom", + "ui-tabs-hide" + ].join( " " ) ); + } + }); + + if ( o.cookie ) { + this._cookie( null, o.cookie ); + } + + return this; + }, + + add: function( url, label, index ) { + if ( index === undefined ) { + index = this.anchors.length; + } + + var self = this, + o = this.options, + $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ), + id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] ); + + $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true ); + + // try to find an existing element before creating a new one + var $panel = self.element.find( "#" + id ); + if ( !$panel.length ) { + $panel = $( o.panelTemplate ) + .attr( "id", id ) + .data( "destroy.tabs", true ); + } + $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" ); + + if ( index >= this.lis.length ) { + $li.appendTo( this.list ); + $panel.appendTo( this.list[ 0 ].parentNode ); + } else { + $li.insertBefore( this.lis[ index ] ); + $panel.insertBefore( this.panels[ index ] ); + } + + o.disabled = $.map( o.disabled, function( n, i ) { + return n >= index ? ++n : n; + }); + + this._tabify(); + + if ( this.anchors.length == 1 ) { + o.selected = 0; + $li.addClass( "ui-tabs-selected ui-state-active" ); + $panel.removeClass( "ui-tabs-hide" ); + this.element.queue( "tabs", function() { + self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) ); + }); + + this.load( 0 ); + } + + this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); + return this; + }, + + remove: function( index ) { + index = this._getIndex( index ); + var o = this.options, + $li = this.lis.eq( index ).remove(), + $panel = this.panels.eq( index ).remove(); + + // If selected tab was removed focus tab to the right or + // in case the last tab was removed the tab to the left. + if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) { + this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) ); + } + + o.disabled = $.map( + $.grep( o.disabled, function(n, i) { + return n != index; + }), + function( n, i ) { + return n >= index ? --n : n; + }); + + this._tabify(); + + this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) ); + return this; + }, + + enable: function( index ) { + index = this._getIndex( index ); + var o = this.options; + if ( $.inArray( index, o.disabled ) == -1 ) { + return; + } + + this.lis.eq( index ).removeClass( "ui-state-disabled" ); + o.disabled = $.grep( o.disabled, function( n, i ) { + return n != index; + }); + + this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); + return this; + }, + + disable: function( index ) { + index = this._getIndex( index ); + var self = this, o = this.options; + // cannot disable already selected tab + if ( index != o.selected ) { + this.lis.eq( index ).addClass( "ui-state-disabled" ); + + o.disabled.push( index ); + o.disabled.sort(); + + this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) ); + } + + return this; + }, + + select: function( index ) { + index = this._getIndex( index ); + if ( index == -1 ) { + if ( this.options.collapsible && this.options.selected != -1 ) { + index = this.options.selected; + } else { + return this; + } + } + this.anchors.eq( index ).trigger( this.options.event + ".tabs" ); + return this; + }, + + load: function( index ) { + index = this._getIndex( index ); + var self = this, + o = this.options, + a = this.anchors.eq( index )[ 0 ], + url = $.data( a, "load.tabs" ); + + this.abort(); + + // not remote or from cache + if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) { + this.element.dequeue( "tabs" ); + return; + } + + // load remote from here on + this.lis.eq( index ).addClass( "ui-state-processing" ); + + if ( o.spinner ) { + var span = $( "span", a ); + span.data( "label.tabs", span.html() ).html( o.spinner ); + } + + this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, { + url: url, + success: function( r, s ) { + self.element.find( self._sanitizeSelector( a.hash ) ).html( r ); + + // take care of tab labels + self._cleanup(); + + if ( o.cache ) { + $.data( a, "cache.tabs", true ); + } + + self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) ); + try { + o.ajaxOptions.success( r, s ); + } + catch ( e ) {} + }, + error: function( xhr, s, e ) { + // take care of tab labels + self._cleanup(); + + self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) ); + try { + // Passing index avoid a race condition when this method is + // called after the user has selected another tab. + // Pass the anchor that initiated this request allows + // loadError to manipulate the tab content panel via $(a.hash) + o.ajaxOptions.error( xhr, s, index, a ); + } + catch ( e ) {} + } + } ) ); + + // last, so that load event is fired before show... + self.element.dequeue( "tabs" ); + + return this; + }, + + abort: function() { + // stop possibly running animations + this.element.queue( [] ); + this.panels.stop( false, true ); + + // "tabs" queue must not contain more than two elements, + // which are the callbacks for the latest clicked tab... + this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) ); + + // terminate pending requests from other tabs + if ( this.xhr ) { + this.xhr.abort(); + delete this.xhr; + } + + // take care of tab labels + this._cleanup(); + return this; + }, + + url: function( index, url ) { + this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url ); + return this; + }, + + length: function() { + return this.anchors.length; + } +}); + +$.extend( $.ui.tabs, { + version: "1.8.12" +}); + +/* + * Tabs Extensions + */ + +/* + * Rotate + */ +$.extend( $.ui.tabs.prototype, { + rotation: null, + rotate: function( ms, continuing ) { + var self = this, + o = this.options; + + var rotate = self._rotate || ( self._rotate = function( e ) { + clearTimeout( self.rotation ); + self.rotation = setTimeout(function() { + var t = o.selected; + self.select( ++t < self.anchors.length ? t : 0 ); + }, ms ); + + if ( e ) { + e.stopPropagation(); + } + }); + + var stop = self._unrotate || ( self._unrotate = !continuing + ? function(e) { + if (e.clientX) { // in case of a true click + self.rotate(null); + } + } + : function( e ) { + t = o.selected; + rotate(); + }); + + // start rotation + if ( ms ) { + this.element.bind( "tabsshow", rotate ); + this.anchors.bind( o.event + ".tabs", stop ); + rotate(); + // stop rotation + } else { + clearTimeout( self.rotation ); + this.element.unbind( "tabsshow", rotate ); + this.anchors.unbind( o.event + ".tabs", stop ); + delete this._rotate; + delete this._unrotate; + } + + return this; + } +}); + +})( jQuery ); diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/jquery-ui.min.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/jquery-ui.min.js new file mode 100644 index 00000000000..33f5bfa2617 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/jquery-ui.min.js @@ -0,0 +1,406 @@ +/*! + * jQuery UI 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(b,d){function e(f){return!b(f).parents().andSelf().filter(function(){return b.curCSS(this,"visibility")==="hidden"||b.expr.filters.hidden(this)}).length}b.ui=b.ui||{};if(!b.ui.version){b.extend(b.ui,{version:"1.8.12",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, +NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});b.fn.extend({_focus:b.fn.focus,focus:function(f,g){return typeof f==="number"?this.each(function(){var a=this;setTimeout(function(){b(a).focus();g&&g.call(a)},f)}):this._focus.apply(this,arguments)},scrollParent:function(){var f;f=b.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(b.curCSS(this, +"position",1))&&/(auto|scroll)/.test(b.curCSS(this,"overflow",1)+b.curCSS(this,"overflow-y",1)+b.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(b.curCSS(this,"overflow",1)+b.curCSS(this,"overflow-y",1)+b.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!f.length?b(document):f},zIndex:function(f){if(f!==d)return this.css("zIndex",f);if(this.length){f=b(this[0]);for(var g;f.length&&f[0]!==document;){g=f.css("position"); +if(g==="absolute"||g==="relative"||g==="fixed"){g=parseInt(f.css("zIndex"),10);if(!isNaN(g)&&g!==0)return g}f=f.parent()}}return 0},disableSelection:function(){return this.bind((b.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(f){f.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});b.each(["Width","Height"],function(f,g){function a(j,n,o,l){b.each(c,function(){n-=parseFloat(b.curCSS(j,"padding"+this,true))||0;if(o)n-=parseFloat(b.curCSS(j, +"border"+this+"Width",true))||0;if(l)n-=parseFloat(b.curCSS(j,"margin"+this,true))||0});return n}var c=g==="Width"?["Left","Right"]:["Top","Bottom"],h=g.toLowerCase(),i={innerWidth:b.fn.innerWidth,innerHeight:b.fn.innerHeight,outerWidth:b.fn.outerWidth,outerHeight:b.fn.outerHeight};b.fn["inner"+g]=function(j){if(j===d)return i["inner"+g].call(this);return this.each(function(){b(this).css(h,a(this,j)+"px")})};b.fn["outer"+g]=function(j,n){if(typeof j!=="number")return i["outer"+g].call(this,j);return this.each(function(){b(this).css(h, +a(this,j,true,n)+"px")})}});b.extend(b.expr[":"],{data:function(f,g,a){return!!b.data(f,a[3])},focusable:function(f){var g=f.nodeName.toLowerCase(),a=b.attr(f,"tabindex");if("area"===g){g=f.parentNode;a=g.name;if(!f.href||!a||g.nodeName.toLowerCase()!=="map")return false;f=b("img[usemap=#"+a+"]")[0];return!!f&&e(f)}return(/input|select|textarea|button|object/.test(g)?!f.disabled:"a"==g?f.href||!isNaN(a):!isNaN(a))&&e(f)},tabbable:function(f){var g=b.attr(f,"tabindex");return(isNaN(g)||g>=0)&&b(f).is(":focusable")}}); +b(function(){var f=document.body,g=f.appendChild(g=document.createElement("div"));b.extend(g.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});b.support.minHeight=g.offsetHeight===100;b.support.selectstart="onselectstart"in g;f.removeChild(g).style.display="none"});b.extend(b.ui,{plugin:{add:function(f,g,a){f=b.ui[f].prototype;for(var c in a){f.plugins[c]=f.plugins[c]||[];f.plugins[c].push([g,a[c]])}},call:function(f,g,a){if((g=f.plugins[g])&&f.element[0].parentNode)for(var c=0;c0)return true;f[g]=1;a=f[g]>0;f[g]=0;return a},isOverAxis:function(f,g,a){return f>g&&f=9)&&!d.button)return this._mouseUp(d);if(this._mouseStarted){this._mouseDrag(d);return d.preventDefault()}if(this._mouseDistanceMet(d)&&this._mouseDelayMet(d))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,d)!==false)?this._mouseDrag(d):this._mouseUp(d);return!this._mouseStarted},_mouseUp:function(d){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate); +if(this._mouseStarted){this._mouseStarted=false;d.target==this._mouseDownEvent.target&&b.data(d.target,this.widgetName+".preventClickEvent",true);this._mouseStop(d)}return false},_mouseDistanceMet:function(d){return Math.max(Math.abs(this._mouseDownEvent.pageX-d.pageX),Math.abs(this._mouseDownEvent.pageY-d.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +(function(b){b.widget("ui.draggable",b.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(d){var e= +this.options;if(this.helper||e.disabled||b(d.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(d);if(!this.handle)return false;return true},_mouseStart:function(d){var e=this.options;this.helper=this._createHelper(d);this._cacheHelperProportions();if(b.ui.ddmanager)b.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top- +this.margins.top,left:this.offset.left-this.margins.left};b.extend(this.offset,{click:{left:d.pageX-this.offset.left,top:d.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(d);this.originalPageX=d.pageX;this.originalPageY=d.pageY;e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt);e.containment&&this._setContainment();if(this._trigger("start",d)===false){this._clear();return false}this._cacheHelperProportions(); +b.ui.ddmanager&&!e.dropBehaviour&&b.ui.ddmanager.prepareOffsets(this,d);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(d,true);return true},_mouseDrag:function(d,e){this.position=this._generatePosition(d);this.positionAbs=this._convertPositionTo("absolute");if(!e){e=this._uiHash();if(this._trigger("drag",d,e)===false){this._mouseUp({});return false}this.position=e.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis|| +this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";b.ui.ddmanager&&b.ui.ddmanager.drag(this,d);return false},_mouseStop:function(d){var e=false;if(b.ui.ddmanager&&!this.options.dropBehaviour)e=b.ui.ddmanager.drop(this,d);if(this.dropped){e=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!e||this.options.revert=="valid"&&e||this.options.revert===true||b.isFunction(this.options.revert)&& +this.options.revert.call(this.element,e)){var f=this;b(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){f._trigger("stop",d)!==false&&f._clear()})}else this._trigger("stop",d)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(d){var e=!this.options.handle||!b(this.options.handle,this.element).length?true:false;b(this.options.handle,this.element).find("*").andSelf().each(function(){if(this== +d.target)e=true});return e},_createHelper:function(d){var e=this.options;d=b.isFunction(e.helper)?b(e.helper.apply(this.element[0],[d])):e.helper=="clone"?this.element.clone():this.element;d.parents("body").length||d.appendTo(e.appendTo=="parent"?this.element[0].parentNode:e.appendTo);d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute");return d},_adjustOffsetFromHelper:function(d){if(typeof d=="string")d=d.split(" ");if(b.isArray(d))d={left:+d[0],top:+d[1]|| +0};if("left"in d)this.offset.click.left=d.left+this.margins.left;if("right"in d)this.offset.click.left=this.helperProportions.width-d.right+this.margins.left;if("top"in d)this.offset.click.top=d.top+this.margins.top;if("bottom"in d)this.offset.click.top=this.helperProportions.height-d.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var d=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&b.ui.contains(this.scrollParent[0], +this.offsetParent[0])){d.left+=this.scrollParent.scrollLeft();d.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&b.browser.msie)d={top:0,left:0};return{top:d.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:d.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var d=this.element.position();return{top:d.top- +(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:d.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(), +height:this.helper.outerHeight()}},_setContainment:function(){var d=this.options;if(d.containment=="parent")d.containment=this.helper[0].parentNode;if(d.containment=="document"||d.containment=="window")this.containment=[(d.containment=="document"?0:b(window).scrollLeft())-this.offset.relative.left-this.offset.parent.left,(d.containment=="document"?0:b(window).scrollTop())-this.offset.relative.top-this.offset.parent.top,(d.containment=="document"?0:b(window).scrollLeft())+b(d.containment=="document"? +document:window).width()-this.helperProportions.width-this.margins.left,(d.containment=="document"?0:b(window).scrollTop())+(b(d.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(d.containment)&&d.containment.constructor!=Array){var e=b(d.containment)[0];if(e){d=b(d.containment).offset();var f=b(e).css("overflow")!="hidden";this.containment=[d.left+(parseInt(b(e).css("borderLeftWidth"), +10)||0)+(parseInt(b(e).css("paddingLeft"),10)||0),d.top+(parseInt(b(e).css("borderTopWidth"),10)||0)+(parseInt(b(e).css("paddingTop"),10)||0),d.left+(f?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(b(e).css("borderLeftWidth"),10)||0)-(parseInt(b(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,d.top+(f?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(b(e).css("borderTopWidth"),10)||0)-(parseInt(b(e).css("paddingBottom"), +10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom]}}else if(d.containment.constructor==Array)this.containment=d.containment},_convertPositionTo:function(d,e){if(!e)e=this.position;d=d=="absolute"?1:-1;var f=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&b.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:e.top+this.offset.relative.top*d+this.offset.parent.top*d-(b.browser.safari&& +b.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:e.left+this.offset.relative.left*d+this.offset.parent.left*d-(b.browser.safari&&b.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(d){var e=this.options,f=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&b.ui.contains(this.scrollParent[0], +this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName),a=d.pageX,c=d.pageY;if(this.originalPosition){if(this.containment){if(d.pageX-this.offset.click.leftthis.containment[2])a=this.containment[2]+this.offset.click.left;if(d.pageY-this.offset.click.top>this.containment[3])c= +this.containment[3]+this.offset.click.top}if(e.grid){c=this.originalPageY+Math.round((c-this.originalPageY)/e.grid[1])*e.grid[1];c=this.containment?!(c-this.offset.click.topthis.containment[3])?c:!(c-this.offset.click.topthis.containment[2])? +a:!(a-this.offset.click.left').css({width:this.offsetWidth+ +"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(b(this).offset()).appendTo("body")})},stop:function(){b("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});b.ui.plugin.add("draggable","opacity",{start:function(d,e){d=b(e.helper);e=b(this).data("draggable").options;if(d.css("opacity"))e._opacity=d.css("opacity");d.css("opacity",e.opacity)},stop:function(d,e){d=b(this).data("draggable").options;d._opacity&&b(e.helper).css("opacity", +d._opacity)}});b.ui.plugin.add("draggable","scroll",{start:function(){var d=b(this).data("draggable");if(d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML")d.overflowOffset=d.scrollParent.offset()},drag:function(d){var e=b(this).data("draggable"),f=e.options,g=false;if(e.scrollParent[0]!=document&&e.scrollParent[0].tagName!="HTML"){if(!f.axis||f.axis!="x")if(e.overflowOffset.top+e.scrollParent[0].offsetHeight-d.pageY=0;n--){var o=f.snapElements[n].left,l=o+f.snapElements[n].width,k=f.snapElements[n].top,m=k+f.snapElements[n].height;if(o-a=n&&c<=o||h>=n&&h<=o||co)&&(g>= +i&&g<=j||a>=i&&a<=j||gj);default:return false}};b.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(d,e){var f=b.ui.ddmanager.droppables[d.options.scope]||[],g=e?e.type:null,a=(d.currentItem||d.element).find(":data(droppable)").andSelf(),c=0;a:for(;c').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), +top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= +this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=g.handles||(!b(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", +nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var a=this.handles.split(",");this.handles={};for(var c=0;c');/sw|se|ne|nw/.test(h)&&i.css({zIndex:++g.zIndex});"se"==h&&i.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[h]=".ui-resizable-"+h;this.element.append(i)}}this._renderAxis=function(j){j=j||this.element;for(var n in this.handles){if(this.handles[n].constructor== +String)this.handles[n]=b(this.handles[n],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var o=b(this.handles[n],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(n)?o.outerHeight():o.outerWidth();o=["padding",/ne|nw|n/.test(n)?"Top":/se|sw|s/.test(n)?"Bottom":/^e$/.test(n)?"Right":"Left"].join("");j.css(o,l);this._proportionallyResize()}b(this.handles[n])}};this._renderAxis(this.element);this._handles=b(".ui-resizable-handle",this.element).disableSelection(); +this._handles.mouseover(function(){if(!f.resizing){if(this.className)var j=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);f.axis=j&&j[1]?j[1]:"se"}});if(g.autoHide){this._handles.hide();b(this.element).addClass("ui-resizable-autohide").hover(function(){b(this).removeClass("ui-resizable-autohide");f._handles.show()},function(){if(!f.resizing){b(this).addClass("ui-resizable-autohide");f._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var f=function(a){b(a).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()}; +if(this.elementIsWrapper){f(this.element);var g=this.element;g.after(this.originalElement.css({position:g.css("position"),width:g.outerWidth(),height:g.outerHeight(),top:g.css("top"),left:g.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);f(this.originalElement);return this},_mouseCapture:function(f){var g=false;for(var a in this.handles)if(b(this.handles[a])[0]==f.target)g=true;return!this.options.disabled&&g},_mouseStart:function(f){var g=this.options,a=this.element.position(), +c=this.element;this.resizing=true;this.documentScroll={top:b(document).scrollTop(),left:b(document).scrollLeft()};if(c.is(".ui-draggable")||/absolute/.test(c.css("position")))c.css({position:"absolute",top:a.top,left:a.left});b.browser.opera&&/relative/.test(c.css("position"))&&c.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();a=d(this.helper.css("left"));var h=d(this.helper.css("top"));if(g.containment){a+=b(g.containment).scrollLeft()||0;h+=b(g.containment).scrollTop()||0}this.offset= +this.helper.offset();this.position={left:a,top:h};this.size=this._helper?{width:c.outerWidth(),height:c.outerHeight()}:{width:c.width(),height:c.height()};this.originalSize=this._helper?{width:c.outerWidth(),height:c.outerHeight()}:{width:c.width(),height:c.height()};this.originalPosition={left:a,top:h};this.sizeDiff={width:c.outerWidth()-c.width(),height:c.outerHeight()-c.height()};this.originalMousePosition={left:f.pageX,top:f.pageY};this.aspectRatio=typeof g.aspectRatio=="number"?g.aspectRatio: +this.originalSize.width/this.originalSize.height||1;g=b(".ui-resizable-"+this.axis).css("cursor");b("body").css("cursor",g=="auto"?this.axis+"-resize":g);c.addClass("ui-resizable-resizing");this._propagate("start",f);return true},_mouseDrag:function(f){var g=this.helper,a=this.originalMousePosition,c=this._change[this.axis];if(!c)return false;a=c.apply(this,[f,f.pageX-a.left||0,f.pageY-a.top||0]);if(this._aspectRatio||f.shiftKey)a=this._updateRatio(a,f);a=this._respectSize(a,f);this._propagate("resize", +f);g.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(a);this._trigger("resize",f,this.ui());return false},_mouseStop:function(f){this.resizing=false;var g=this.options,a=this;if(this._helper){var c=this._proportionallyResizeElements,h=c.length&&/textarea/i.test(c[0].nodeName);c=h&&b.ui.hasScroll(c[0],"left")?0:a.sizeDiff.height; +h=h?0:a.sizeDiff.width;h={width:a.helper.width()-h,height:a.helper.height()-c};c=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var i=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;g.animate||this.element.css(b.extend(h,{top:i,left:c}));a.helper.height(a.size.height);a.helper.width(a.size.width);this._helper&&!g.animate&&this._proportionallyResize()}b("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing"); +this._propagate("stop",f);this._helper&&this.helper.remove();return false},_updateCache:function(f){this.offset=this.helper.offset();if(e(f.left))this.position.left=f.left;if(e(f.top))this.position.top=f.top;if(e(f.height))this.size.height=f.height;if(e(f.width))this.size.width=f.width},_updateRatio:function(f){var g=this.position,a=this.size,c=this.axis;if(f.height)f.width=a.height*this.aspectRatio;else if(f.width)f.height=a.width/this.aspectRatio;if(c=="sw"){f.left=g.left+(a.width-f.width);f.top= +null}if(c=="nw"){f.top=g.top+(a.height-f.height);f.left=g.left+(a.width-f.width)}return f},_respectSize:function(f){var g=this.options,a=this.axis,c=e(f.width)&&g.maxWidth&&g.maxWidthf.width,j=e(f.height)&&g.minHeight&&g.minHeight>f.height;if(i)f.width=g.minWidth;if(j)f.height=g.minHeight;if(c)f.width=g.maxWidth;if(h)f.height=g.maxHeight;var n=this.originalPosition.left+this.originalSize.width,o=this.position.top+ +this.size.height,l=/sw|nw|w/.test(a);a=/nw|ne|n/.test(a);if(i&&l)f.left=n-g.minWidth;if(c&&l)f.left=n-g.maxWidth;if(j&&a)f.top=o-g.minHeight;if(h&&a)f.top=o-g.maxHeight;if((g=!f.width&&!f.height)&&!f.left&&f.top)f.top=null;else if(g&&!f.top&&f.left)f.left=null;return f},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var f=this.helper||this.element,g=0;g');var g=b.browser.msie&&b.browser.version<7,a=g?1:0;g=g?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+g,height:this.element.outerHeight()+g,position:"absolute",left:this.elementOffset.left-a+"px",top:this.elementOffset.top-a+"px",zIndex:++f.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(f, +g){return{width:this.originalSize.width+g}},w:function(f,g){return{left:this.originalPosition.left+g,width:this.originalSize.width-g}},n:function(f,g,a){return{top:this.originalPosition.top+a,height:this.originalSize.height-a}},s:function(f,g,a){return{height:this.originalSize.height+a}},se:function(f,g,a){return b.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[f,g,a]))},sw:function(f,g,a){return b.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[f,g, +a]))},ne:function(f,g,a){return b.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[f,g,a]))},nw:function(f,g,a){return b.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[f,g,a]))}},_propagate:function(f,g){b.ui.plugin.call(this,f,[g,this.ui()]);f!="resize"&&this._trigger(f,g,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize, +originalPosition:this.originalPosition}}});b.extend(b.ui.resizable,{version:"1.8.12"});b.ui.plugin.add("resizable","alsoResize",{start:function(){var f=b(this).data("resizable").options,g=function(a){b(a).each(function(){var c=b(this);c.data("resizable-alsoresize",{width:parseInt(c.width(),10),height:parseInt(c.height(),10),left:parseInt(c.css("left"),10),top:parseInt(c.css("top"),10),position:c.css("position")})})};if(typeof f.alsoResize=="object"&&!f.alsoResize.parentNode)if(f.alsoResize.length){f.alsoResize= +f.alsoResize[0];g(f.alsoResize)}else b.each(f.alsoResize,function(a){g(a)});else g(f.alsoResize)},resize:function(f,g){var a=b(this).data("resizable");f=a.options;var c=a.originalSize,h=a.originalPosition,i={height:a.size.height-c.height||0,width:a.size.width-c.width||0,top:a.position.top-h.top||0,left:a.position.left-h.left||0},j=function(n,o){b(n).each(function(){var l=b(this),k=b(this).data("resizable-alsoresize"),m={},p=o&&o.length?o:l.parents(g.originalElement[0]).length?["width","height"]:["width", +"height","top","left"];b.each(p,function(q,s){if((q=(k[s]||0)+(i[s]||0))&&q>=0)m[s]=q||null});if(b.browser.opera&&/relative/.test(l.css("position"))){a._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(m)})};typeof f.alsoResize=="object"&&!f.alsoResize.nodeType?b.each(f.alsoResize,function(n,o){j(n,o)}):j(f.alsoResize)},stop:function(){var f=b(this).data("resizable"),g=f.options,a=function(c){b(c).each(function(){var h=b(this);h.css({position:h.data("resizable-alsoresize").position})})}; +if(f._revertToRelativePosition){f._revertToRelativePosition=false;typeof g.alsoResize=="object"&&!g.alsoResize.nodeType?b.each(g.alsoResize,function(c){a(c)}):a(g.alsoResize)}b(this).removeData("resizable-alsoresize")}});b.ui.plugin.add("resizable","animate",{stop:function(f){var g=b(this).data("resizable"),a=g.options,c=g._proportionallyResizeElements,h=c.length&&/textarea/i.test(c[0].nodeName),i=h&&b.ui.hasScroll(c[0],"left")?0:g.sizeDiff.height;h={width:g.size.width-(h?0:g.sizeDiff.width),height:g.size.height- +i};i=parseInt(g.element.css("left"),10)+(g.position.left-g.originalPosition.left)||null;var j=parseInt(g.element.css("top"),10)+(g.position.top-g.originalPosition.top)||null;g.element.animate(b.extend(h,j&&i?{top:j,left:i}:{}),{duration:a.animateDuration,easing:a.animateEasing,step:function(){var n={width:parseInt(g.element.css("width"),10),height:parseInt(g.element.css("height"),10),top:parseInt(g.element.css("top"),10),left:parseInt(g.element.css("left"),10)};c&&c.length&&b(c[0]).css({width:n.width, +height:n.height});g._updateCache(n);g._propagate("resize",f)}})}});b.ui.plugin.add("resizable","containment",{start:function(){var f=b(this).data("resizable"),g=f.element,a=f.options.containment;if(g=a instanceof b?a.get(0):/parent/.test(a)?g.parent().get(0):a){f.containerElement=b(g);if(/document/.test(a)||a==document){f.containerOffset={left:0,top:0};f.containerPosition={left:0,top:0};f.parentData={element:b(document),left:0,top:0,width:b(document).width(),height:b(document).height()||document.body.parentNode.scrollHeight}}else{var c= +b(g),h=[];b(["Top","Right","Left","Bottom"]).each(function(n,o){h[n]=d(c.css("padding"+o))});f.containerOffset=c.offset();f.containerPosition=c.position();f.containerSize={height:c.innerHeight()-h[3],width:c.innerWidth()-h[1]};a=f.containerOffset;var i=f.containerSize.height,j=f.containerSize.width;j=b.ui.hasScroll(g,"left")?g.scrollWidth:j;i=b.ui.hasScroll(g)?g.scrollHeight:i;f.parentData={element:g,left:a.left,top:a.top,width:j,height:i}}}},resize:function(f){var g=b(this).data("resizable"),a=g.options, +c=g.containerOffset,h=g.position;f=g._aspectRatio||f.shiftKey;var i={top:0,left:0},j=g.containerElement;if(j[0]!=document&&/static/.test(j.css("position")))i=c;if(h.left<(g._helper?c.left:0)){g.size.width+=g._helper?g.position.left-c.left:g.position.left-i.left;if(f)g.size.height=g.size.width/a.aspectRatio;g.position.left=a.helper?c.left:0}if(h.top<(g._helper?c.top:0)){g.size.height+=g._helper?g.position.top-c.top:g.position.top;if(f)g.size.width=g.size.height*a.aspectRatio;g.position.top=g._helper? +c.top:0}g.offset.left=g.parentData.left+g.position.left;g.offset.top=g.parentData.top+g.position.top;a=Math.abs((g._helper?g.offset.left-i.left:g.offset.left-i.left)+g.sizeDiff.width);c=Math.abs((g._helper?g.offset.top-i.top:g.offset.top-c.top)+g.sizeDiff.height);h=g.containerElement.get(0)==g.element.parent().get(0);i=/relative|absolute/.test(g.containerElement.css("position"));if(h&&i)a-=g.parentData.left;if(a+g.size.width>=g.parentData.width){g.size.width=g.parentData.width-a;if(f)g.size.height= +g.size.width/g.aspectRatio}if(c+g.size.height>=g.parentData.height){g.size.height=g.parentData.height-c;if(f)g.size.width=g.size.height*g.aspectRatio}},stop:function(){var f=b(this).data("resizable"),g=f.options,a=f.containerOffset,c=f.containerPosition,h=f.containerElement,i=b(f.helper),j=i.offset(),n=i.outerWidth()-f.sizeDiff.width;i=i.outerHeight()-f.sizeDiff.height;f._helper&&!g.animate&&/relative/.test(h.css("position"))&&b(this).css({left:j.left-c.left-a.left,width:n,height:i});f._helper&&!g.animate&& +/static/.test(h.css("position"))&&b(this).css({left:j.left-c.left-a.left,width:n,height:i})}});b.ui.plugin.add("resizable","ghost",{start:function(){var f=b(this).data("resizable"),g=f.options,a=f.size;f.ghost=f.originalElement.clone();f.ghost.css({opacity:0.25,display:"block",position:"relative",height:a.height,width:a.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof g.ghost=="string"?g.ghost:"");f.ghost.appendTo(f.helper)},resize:function(){var f=b(this).data("resizable"); +f.ghost&&f.ghost.css({position:"relative",height:f.size.height,width:f.size.width})},stop:function(){var f=b(this).data("resizable");f.ghost&&f.helper&&f.helper.get(0).removeChild(f.ghost.get(0))}});b.ui.plugin.add("resizable","grid",{resize:function(){var f=b(this).data("resizable"),g=f.options,a=f.size,c=f.originalSize,h=f.originalPosition,i=f.axis;g.grid=typeof g.grid=="number"?[g.grid,g.grid]:g.grid;var j=Math.round((a.width-c.width)/(g.grid[0]||1))*(g.grid[0]||1);g=Math.round((a.height-c.height)/ +(g.grid[1]||1))*(g.grid[1]||1);if(/^(se|s|e)$/.test(i)){f.size.width=c.width+j;f.size.height=c.height+g}else if(/^(ne)$/.test(i)){f.size.width=c.width+j;f.size.height=c.height+g;f.position.top=h.top-g}else{if(/^(sw)$/.test(i)){f.size.width=c.width+j;f.size.height=c.height+g}else{f.size.width=c.width+j;f.size.height=c.height+g;f.position.top=h.top-g}f.position.left=h.left-j}}});var d=function(f){return parseInt(f,10)||0},e=function(f){return!isNaN(parseInt(f,10))}})(jQuery); +(function(b){b.widget("ui.selectable",b.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var d=this;this.element.addClass("ui-selectable");this.dragged=false;var e;this.refresh=function(){e=b(d.options.filter,d.element[0]);e.each(function(){var f=b(this),g=f.offset();b.data(this,"selectable-item",{element:this,$element:f,left:g.left,top:g.top,right:g.left+f.outerWidth(),bottom:g.top+f.outerHeight(),startselected:false,selected:f.hasClass("ui-selected"), +selecting:f.hasClass("ui-selecting"),unselecting:f.hasClass("ui-unselecting")})})};this.refresh();this.selectees=e.addClass("ui-selectee");this._mouseInit();this.helper=b("
      ")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(d){var e=this;this.opos=[d.pageX, +d.pageY];if(!this.options.disabled){var f=this.options;this.selectees=b(f.filter,this.element[0]);this._trigger("start",d);b(f.appendTo).append(this.helper);this.helper.css({left:d.clientX,top:d.clientY,width:0,height:0});f.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var g=b.data(this,"selectable-item");g.startselected=true;if(!d.metaKey){g.$element.removeClass("ui-selected");g.selected=false;g.$element.addClass("ui-unselecting");g.unselecting=true;e._trigger("unselecting", +d,{unselecting:g.element})}});b(d.target).parents().andSelf().each(function(){var g=b.data(this,"selectable-item");if(g){var a=!d.metaKey||!g.$element.hasClass("ui-selected");g.$element.removeClass(a?"ui-unselecting":"ui-selected").addClass(a?"ui-selecting":"ui-unselecting");g.unselecting=!a;g.selecting=a;(g.selected=a)?e._trigger("selecting",d,{selecting:g.element}):e._trigger("unselecting",d,{unselecting:g.element});return false}})}},_mouseDrag:function(d){var e=this;this.dragged=true;if(!this.options.disabled){var f= +this.options,g=this.opos[0],a=this.opos[1],c=d.pageX,h=d.pageY;if(g>c){var i=c;c=g;g=i}if(a>h){i=h;h=a;a=i}this.helper.css({left:g,top:a,width:c-g,height:h-a});this.selectees.each(function(){var j=b.data(this,"selectable-item");if(!(!j||j.element==e.element[0])){var n=false;if(f.tolerance=="touch")n=!(j.left>c||j.righth||j.bottomg&&j.righta&&j.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); +this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var d=this.items.length-1;d>=0;d--)this.items[d].item.removeData("sortable-item");return this},_setOption:function(d,e){if(d==="disabled"){this.options[d]= +e;this.widget()[e?"addClass":"removeClass"]("ui-sortable-disabled")}else b.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(d,e){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(d);var f=null,g=this;b(d.target).parents().each(function(){if(b.data(this,"sortable-item")==g){f=b(this);return false}});if(b.data(d.target,"sortable-item")==g)f=b(d.target);if(!f)return false;if(this.options.handle&&!e){var a=false; +b(this.options.handle,f).find("*").andSelf().each(function(){if(this==d.target)a=true});if(!a)return false}this.currentItem=f;this._removeCurrentsFromItems();return true},_mouseStart:function(d,e,f){e=this.options;var g=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(d);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left- +this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");b.extend(this.offset,{click:{left:d.pageX-this.offset.left,top:d.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(d);this.originalPageX=d.pageX;this.originalPageY=d.pageY;e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]}; +this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();e.containment&&this._setContainment();if(e.cursor){if(b("body").css("cursor"))this._storedCursor=b("body").css("cursor");b("body").css("cursor",e.cursor)}if(e.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",e.opacity)}if(e.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",e.zIndex)}if(this.scrollParent[0]!= +document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",d,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!f)for(f=this.containers.length-1;f>=0;f--)this.containers[f]._trigger("activate",d,g._uiHash(this));if(b.ui.ddmanager)b.ui.ddmanager.current=this;b.ui.ddmanager&&!e.dropBehaviour&&b.ui.ddmanager.prepareOffsets(this,d);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(d); +return true},_mouseDrag:function(d){this.position=this._generatePosition(d);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var e=this.options,f=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-d.pageY=0;e--){f=this.items[e];var g=f.item[0],a=this._intersectsWithPointer(f);if(a)if(g!=this.currentItem[0]&&this.placeholder[a==1?"next":"prev"]()[0]!=g&&!b.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!b.ui.contains(this.element[0], +g):true)){this.direction=a==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(d,f);else break;this._trigger("change",d,this._uiHash());break}}this._contactContainers(d);b.ui.ddmanager&&b.ui.ddmanager.drag(this,d);this._trigger("sort",d,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(d,e){if(d){b.ui.ddmanager&&!this.options.dropBehaviour&&b.ui.ddmanager.drop(this,d);if(this.options.revert){var f=this;e=f.placeholder.offset(); +f.reverting=true;b(this.helper).animate({left:e.left-this.offset.parent.left-f.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-f.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){f._clear(d)})}else this._clear(d,e);return false}},cancel:function(){var d=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"): +this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--){this.containers[e]._trigger("deactivate",null,d._uiHash(this));if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",null,d._uiHash(this));this.containers[e].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();b.extend(this,{helper:null, +dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?b(this.domPosition.prev).after(this.currentItem):b(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(d){var e=this._getItemsAsjQuery(d&&d.connected),f=[];d=d||{};b(e).each(function(){var g=(b(d.item||this).attr(d.attribute||"id")||"").match(d.expression||/(.+)[-=_](.+)/);if(g)f.push((d.key||g[1]+"[]")+"="+(d.key&&d.expression?g[1]:g[2]))});!f.length&&d.key&&f.push(d.key+"=");return f.join("&")}, +toArray:function(d){var e=this._getItemsAsjQuery(d&&d.connected),f=[];d=d||{};e.each(function(){f.push(b(d.item||this).attr(d.attribute||"id")||"")});return f},_intersectsWith:function(d){var e=this.positionAbs.left,f=e+this.helperProportions.width,g=this.positionAbs.top,a=g+this.helperProportions.height,c=d.left,h=c+d.width,i=d.top,j=i+d.height,n=this.offset.click.top,o=this.offset.click.left;n=g+n>i&&g+nc&&e+od[this.floating?"width":"height"]?n:c0?"down":"up")},_getDragHorizontalDirection:function(){var d=this.positionAbs.left-this.lastPositionAbs.left;return d!=0&&(d>0?"right":"left")},refresh:function(d){this._refreshItems(d);this.refreshPositions();return this},_connectWith:function(){var d=this.options;return d.connectWith.constructor==String?[d.connectWith]:d.connectWith},_getItemsAsjQuery:function(d){var e=[],f=[],g=this._connectWith(); +if(g&&d)for(d=g.length-1;d>=0;d--)for(var a=b(g[d]),c=a.length-1;c>=0;c--){var h=b.data(a[c],"sortable");if(h&&h!=this&&!h.options.disabled)f.push([b.isFunction(h.options.items)?h.options.items.call(h.element):b(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}f.push([b.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):b(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), +this]);for(d=f.length-1;d>=0;d--)f[d][0].each(function(){e.push(this)});return b(e)},_removeCurrentsFromItems:function(){for(var d=this.currentItem.find(":data(sortable-item)"),e=0;e=0;a--)for(var c=b(g[a]),h=c.length-1;h>=0;h--){var i=b.data(c[h],"sortable");if(i&&i!=this&&!i.options.disabled){f.push([b.isFunction(i.options.items)?i.options.items.call(i.element[0],d,{item:this.currentItem}):b(i.options.items,i.element),i]);this.containers.push(i)}}for(a=f.length-1;a>=0;a--){d=f[a][1];g=f[a][0];h=0;for(c=g.length;h=0;e--){var f=this.items[e];if(!(f.instance!=this.currentContainer&&this.currentContainer&&f.item[0]!=this.currentItem[0])){var g=this.options.toleranceElement?b(this.options.toleranceElement,f.item):f.item;if(!d){f.width=g.outerWidth();f.height=g.outerHeight()}g=g.offset();f.left=g.left;f.top=g.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(e= +this.containers.length-1;e>=0;e--){g=this.containers[e].element.offset();this.containers[e].containerCache.left=g.left;this.containers[e].containerCache.top=g.top;this.containers[e].containerCache.width=this.containers[e].element.outerWidth();this.containers[e].containerCache.height=this.containers[e].element.outerHeight()}return this},_createPlaceholder:function(d){var e=d||this,f=e.options;if(!f.placeholder||f.placeholder.constructor==String){var g=f.placeholder;f.placeholder={element:function(){var a= +b(document.createElement(e.currentItem[0].nodeName)).addClass(g||e.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!g)a.style.visibility="hidden";return a},update:function(a,c){if(!(g&&!f.forcePlaceholderSize)){c.height()||c.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10));c.width()||c.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")|| +0,10))}}}}e.placeholder=b(f.placeholder.element.call(e.element,e.currentItem));e.currentItem.after(e.placeholder);f.placeholder.update(e,e.placeholder)},_contactContainers:function(d){for(var e=null,f=null,g=this.containers.length-1;g>=0;g--)if(!b.ui.contains(this.currentItem[0],this.containers[g].element[0]))if(this._intersectsWith(this.containers[g].containerCache)){if(!(e&&b.ui.contains(this.containers[g].element[0],e.element[0]))){e=this.containers[g];f=g}}else if(this.containers[g].containerCache.over){this.containers[g]._trigger("out", +d,this._uiHash(this));this.containers[g].containerCache.over=0}if(e)if(this.containers.length===1){this.containers[f]._trigger("over",d,this._uiHash(this));this.containers[f].containerCache.over=1}else if(this.currentContainer!=this.containers[f]){e=1E4;g=null;for(var a=this.positionAbs[this.containers[f].floating?"left":"top"],c=this.items.length-1;c>=0;c--)if(b.ui.contains(this.containers[f].element[0],this.items[c].item[0])){var h=this.items[c][this.containers[f].floating?"left":"top"];if(Math.abs(h- +a)this.containment[2])a=this.containment[2]+this.offset.click.left;if(d.pageY-this.offset.click.top>this.containment[3])c=this.containment[3]+this.offset.click.top}if(e.grid){c=this.originalPageY+Math.round((c- +this.originalPageY)/e.grid[1])*e.grid[1];c=this.containment?!(c-this.offset.click.topthis.containment[3])?c:!(c-this.offset.click.topthis.containment[2])?a:!(a-this.offset.click.left=0;g--)if(b.ui.contains(this.containers[g].element[0],this.currentItem[0])&&!e){f.push(function(a){return function(c){a._trigger("receive",c,this._uiHash(this))}}.call(this,this.containers[g]));f.push(function(a){return function(c){a._trigger("update",c,this._uiHash(this))}}.call(this,this.containers[g]))}}for(g=this.containers.length-1;g>=0;g--){e||f.push(function(a){return function(c){a._trigger("deactivate",c,this._uiHash(this))}}.call(this, +this.containers[g]));if(this.containers[g].containerCache.over){f.push(function(a){return function(c){a._trigger("out",c,this._uiHash(this))}}.call(this,this.containers[g]));this.containers[g].containerCache.over=0}}this._storedCursor&&b("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!e){this._trigger("beforeStop", +d,this._uiHash());for(g=0;g").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent", +border:"none",margin:0,padding:0});l.wrap(m);m=l.parent();if(l.css("position")=="static"){m.css({position:"relative"});l.css({position:"relative"})}else{b.extend(k,{position:l.css("position"),zIndex:l.css("z-index")});b.each(["top","left","bottom","right"],function(p,q){k[q]=l.css(q);if(isNaN(parseInt(k[q],10)))k[q]="auto"});l.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return m.css(k).show()},removeWrapper:function(l){if(l.parent().is(".ui-effects-wrapper"))return l.parent().replaceWith(l); +return l},setTransition:function(l,k,m,p){p=p||{};b.each(k,function(q,s){unit=l.cssUnit(s);if(unit[0]>0)p[s]=unit[0]*m+unit[1]});return p}});b.fn.extend({effect:function(l){var k=h.apply(this,arguments),m={options:k[1],duration:k[2],callback:k[3]};k=m.options.mode;var p=b.effects[l];if(b.fx.off||!p)return k?this[k](m.duration,m.callback):this.each(function(){m.callback&&m.callback.call(this)});return p.call(this,m)},_show:b.fn.show,show:function(l){if(i(l))return this._show.apply(this,arguments); +else{var k=h.apply(this,arguments);k[1].mode="show";return this.effect.apply(this,k)}},_hide:b.fn.hide,hide:function(l){if(i(l))return this._hide.apply(this,arguments);else{var k=h.apply(this,arguments);k[1].mode="hide";return this.effect.apply(this,k)}},__toggle:b.fn.toggle,toggle:function(l){if(i(l)||typeof l==="boolean"||b.isFunction(l))return this.__toggle.apply(this,arguments);else{var k=h.apply(this,arguments);k[1].mode="toggle";return this.effect.apply(this,k)}},cssUnit:function(l){var k=this.css(l), +m=[];b.each(["em","px","%","pt"],function(p,q){if(k.indexOf(q)>0)m=[parseFloat(k),q]});return m}});b.easing.jswing=b.easing.swing;b.extend(b.easing,{def:"easeOutQuad",swing:function(l,k,m,p,q){return b.easing[b.easing.def](l,k,m,p,q)},easeInQuad:function(l,k,m,p,q){return p*(k/=q)*k+m},easeOutQuad:function(l,k,m,p,q){return-p*(k/=q)*(k-2)+m},easeInOutQuad:function(l,k,m,p,q){if((k/=q/2)<1)return p/2*k*k+m;return-p/2*(--k*(k-2)-1)+m},easeInCubic:function(l,k,m,p,q){return p*(k/=q)*k*k+m},easeOutCubic:function(l, +k,m,p,q){return p*((k=k/q-1)*k*k+1)+m},easeInOutCubic:function(l,k,m,p,q){if((k/=q/2)<1)return p/2*k*k*k+m;return p/2*((k-=2)*k*k+2)+m},easeInQuart:function(l,k,m,p,q){return p*(k/=q)*k*k*k+m},easeOutQuart:function(l,k,m,p,q){return-p*((k=k/q-1)*k*k*k-1)+m},easeInOutQuart:function(l,k,m,p,q){if((k/=q/2)<1)return p/2*k*k*k*k+m;return-p/2*((k-=2)*k*k*k-2)+m},easeInQuint:function(l,k,m,p,q){return p*(k/=q)*k*k*k*k+m},easeOutQuint:function(l,k,m,p,q){return p*((k=k/q-1)*k*k*k*k+1)+m},easeInOutQuint:function(l, +k,m,p,q){if((k/=q/2)<1)return p/2*k*k*k*k*k+m;return p/2*((k-=2)*k*k*k*k+2)+m},easeInSine:function(l,k,m,p,q){return-p*Math.cos(k/q*(Math.PI/2))+p+m},easeOutSine:function(l,k,m,p,q){return p*Math.sin(k/q*(Math.PI/2))+m},easeInOutSine:function(l,k,m,p,q){return-p/2*(Math.cos(Math.PI*k/q)-1)+m},easeInExpo:function(l,k,m,p,q){return k==0?m:p*Math.pow(2,10*(k/q-1))+m},easeOutExpo:function(l,k,m,p,q){return k==q?m+p:p*(-Math.pow(2,-10*k/q)+1)+m},easeInOutExpo:function(l,k,m,p,q){if(k==0)return m;if(k== +q)return m+p;if((k/=q/2)<1)return p/2*Math.pow(2,10*(k-1))+m;return p/2*(-Math.pow(2,-10*--k)+2)+m},easeInCirc:function(l,k,m,p,q){return-p*(Math.sqrt(1-(k/=q)*k)-1)+m},easeOutCirc:function(l,k,m,p,q){return p*Math.sqrt(1-(k=k/q-1)*k)+m},easeInOutCirc:function(l,k,m,p,q){if((k/=q/2)<1)return-p/2*(Math.sqrt(1-k*k)-1)+m;return p/2*(Math.sqrt(1-(k-=2)*k)+1)+m},easeInElastic:function(l,k,m,p,q){l=1.70158;var s=0,r=p;if(k==0)return m;if((k/=q)==1)return m+p;s||(s=q*0.3);if(r").css({position:"absolute",visibility:"visible",left:-j*(c/f),top:-i*(h/e)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:c/f,height:h/e,left:a.left+j*(c/f)+(d.options.mode=="show"?(j-Math.floor(f/2))*(c/f):0),top:a.top+i*(h/e)+(d.options.mode=="show"?(i-Math.floor(e/2))*(h/e):0),opacity:d.options.mode=="show"?0:1}).animate({left:a.left+j*(c/f)+(d.options.mode=="show"?0:(j-Math.floor(f/2))*(c/f)),top:a.top+ +i*(h/e)+(d.options.mode=="show"?0:(i-Math.floor(e/2))*(h/e)),opacity:d.options.mode=="show"?1:0},d.duration||500);setTimeout(function(){d.options.mode=="show"?g.css({visibility:"visible"}):g.css({visibility:"visible"}).hide();d.callback&&d.callback.apply(g[0]);g.dequeue();b("div.ui-effects-explode").remove()},d.duration||500)})}})(jQuery); +(function(b){b.effects.fade=function(d){return this.queue(function(){var e=b(this),f=b.effects.setMode(e,d.options.mode||"hide");e.animate({opacity:f},{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){d.callback&&d.callback.apply(this,arguments);e.dequeue()}})})}})(jQuery); +(function(b){b.effects.fold=function(d){return this.queue(function(){var e=b(this),f=["position","top","bottom","left","right"],g=b.effects.setMode(e,d.options.mode||"hide"),a=d.options.size||15,c=!!d.options.horizFirst,h=d.duration?d.duration/2:b.fx.speeds._default/2;b.effects.save(e,f);e.show();var i=b.effects.createWrapper(e).css({overflow:"hidden"}),j=g=="show"!=c,n=j?["width","height"]:["height","width"];j=j?[i.width(),i.height()]:[i.height(),i.width()];var o=/([0-9]+)%/.exec(a);if(o)a=parseInt(o[1], +10)/100*j[g=="hide"?0:1];if(g=="show")i.css(c?{height:0,width:a}:{height:a,width:0});c={};o={};c[n[0]]=g=="show"?j[0]:a;o[n[1]]=g=="show"?j[1]:0;i.animate(c,h,d.options.easing).animate(o,h,d.options.easing,function(){g=="hide"&&e.hide();b.effects.restore(e,f);b.effects.removeWrapper(e);d.callback&&d.callback.apply(e[0],arguments);e.dequeue()})})}})(jQuery); +(function(b){b.effects.highlight=function(d){return this.queue(function(){var e=b(this),f=["backgroundImage","backgroundColor","opacity"],g=b.effects.setMode(e,d.options.mode||"show"),a={backgroundColor:e.css("backgroundColor")};if(g=="hide")a.opacity=0;b.effects.save(e,f);e.show().css({backgroundImage:"none",backgroundColor:d.options.color||"#ffff99"}).animate(a,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){g=="hide"&&e.hide();b.effects.restore(e,f);g=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");d.callback&&d.callback.apply(this,arguments);e.dequeue()}})})}})(jQuery); +(function(b){b.effects.pulsate=function(d){return this.queue(function(){var e=b(this),f=b.effects.setMode(e,d.options.mode||"show");times=(d.options.times||5)*2-1;duration=d.duration?d.duration/2:b.fx.speeds._default/2;isVisible=e.is(":visible");animateTo=0;if(!isVisible){e.css("opacity",0).show();animateTo=1}if(f=="hide"&&isVisible||f=="show"&&!isVisible)times--;for(f=0;f').appendTo(document.body).addClass(d.options.className).css({top:g.top,left:g.left,height:e.innerHeight(),width:e.innerWidth(),position:"absolute"}).animate(f,d.duration,d.options.easing,function(){a.remove();d.callback&&d.callback.apply(e[0],arguments); +e.dequeue()})})}})(jQuery); +(function(b){b.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var d=this,e=d.options;d.running=0;d.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");d.headers= +d.element.find(e.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){e.disabled||b(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){e.disabled||b(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){e.disabled||b(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){e.disabled||b(this).removeClass("ui-state-focus")});d.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); +if(e.navigation){var f=d.element.find("a").filter(e.navigationFilter).eq(0);if(f.length){var g=f.closest(".ui-accordion-header");d.active=g.length?g:f.closest(".ui-accordion-content").prev()}}d.active=d._findActive(d.active||e.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");d.active.next().addClass("ui-accordion-content-active");d._createIcons();d.resize();d.element.attr("role","tablist");d.headers.attr("role","tab").bind("keydown.accordion", +function(a){return d._keydown(a)}).next().attr("role","tabpanel");d.headers.not(d.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();d.active.length?d.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):d.headers.eq(0).attr("tabIndex",0);b.browser.safari||d.headers.find("a").attr("tabIndex",-1);e.event&&d.headers.bind(e.event.split(" ").join(".accordion ")+".accordion",function(a){d._clickHandler.call(d,a,this);a.preventDefault()})},_createIcons:function(){var d= +this.options;if(d.icons){b("").addClass("ui-icon "+d.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(d.icons.header).toggleClass(d.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var d=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"); +this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var e=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(d.autoHeight||d.fillHeight)e.css("height","");return b.Widget.prototype.destroy.call(this)},_setOption:function(d,e){b.Widget.prototype._setOption.apply(this,arguments);d=="active"&&this.activate(e);if(d=="icons"){this._destroyIcons(); +e&&this._createIcons()}if(d=="disabled")this.headers.add(this.headers.next())[e?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(d){if(!(this.options.disabled||d.altKey||d.ctrlKey)){var e=b.ui.keyCode,f=this.headers.length,g=this.headers.index(d.target),a=false;switch(d.keyCode){case e.RIGHT:case e.DOWN:a=this.headers[(g+1)%f];break;case e.LEFT:case e.UP:a=this.headers[(g-1+f)%f];break;case e.SPACE:case e.ENTER:this._clickHandler({target:d.target},d.target); +d.preventDefault()}if(a){b(d.target).attr("tabIndex",-1);b(a).attr("tabIndex",0);a.focus();return false}return true}},resize:function(){var d=this.options,e;if(d.fillSpace){if(b.browser.msie){var f=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}e=this.element.parent().height();b.browser.msie&&this.element.parent().css("overflow",f);this.headers.each(function(){e-=b(this).outerHeight(true)});this.headers.next().each(function(){b(this).height(Math.max(0,e-b(this).innerHeight()+ +b(this).height()))}).css("overflow","auto")}else if(d.autoHeight){e=0;this.headers.next().each(function(){e=Math.max(e,b(this).height("").height())}).height(e)}return this},activate:function(d){this.options.active=d;d=this._findActive(d)[0];this._clickHandler({target:d},d);return this},_findActive:function(d){return d?typeof d==="number"?this.headers.filter(":eq("+d+")"):this.headers.not(this.headers.not(d)):d===false?b([]):this.headers.filter(":eq(0)")},_clickHandler:function(d,e){var f=this.options; +if(!f.disabled)if(d.target){d=b(d.currentTarget||e);e=d[0]===this.active[0];f.active=f.collapsible&&e?false:this.headers.index(d);if(!(this.running||!f.collapsible&&e)){var g=this.active;i=d.next();c=this.active.next();h={options:f,newHeader:e&&f.collapsible?b([]):d,oldHeader:this.active,newContent:e&&f.collapsible?b([]):i,oldContent:c};var a=this.headers.index(this.active[0])>this.headers.index(d[0]);this.active=e?b([]):d;this._toggle(i,c,h,e,a);g.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(f.icons.headerSelected).addClass(f.icons.header); +if(!e){d.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(f.icons.header).addClass(f.icons.headerSelected);d.next().addClass("ui-accordion-content-active")}}}else if(f.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(f.icons.headerSelected).addClass(f.icons.header);this.active.next().addClass("ui-accordion-content-active");var c=this.active.next(), +h={options:f,newHeader:b([]),oldHeader:f.active,newContent:b([]),oldContent:c},i=this.active=b([]);this._toggle(i,c,h)}},_toggle:function(d,e,f,g,a){var c=this,h=c.options;c.toShow=d;c.toHide=e;c.data=f;var i=function(){if(c)return c._completed.apply(c,arguments)};c._trigger("changestart",null,c.data);c.running=e.size()===0?d.size():e.size();if(h.animated){f={};f=h.collapsible&&g?{toShow:b([]),toHide:e,complete:i,down:a,autoHeight:h.autoHeight||h.fillSpace}:{toShow:d,toHide:e,complete:i,down:a,autoHeight:h.autoHeight|| +h.fillSpace};if(!h.proxied)h.proxied=h.animated;if(!h.proxiedDuration)h.proxiedDuration=h.duration;h.animated=b.isFunction(h.proxied)?h.proxied(f):h.proxied;h.duration=b.isFunction(h.proxiedDuration)?h.proxiedDuration(f):h.proxiedDuration;g=b.ui.accordion.animations;var j=h.duration,n=h.animated;if(n&&!g[n]&&!b.easing[n])n="slide";g[n]||(g[n]=function(o){this.slide(o,{easing:n,duration:j||700})});g[n](f)}else{if(h.collapsible&&g)d.toggle();else{e.hide();d.show()}i(true)}e.prev().attr({"aria-expanded":"false", +"aria-selected":"false",tabIndex:-1}).blur();d.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(d){this.running=d?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});b.extend(b.ui.accordion,{version:"1.8.12", +animations:{slide:function(d,e){d=b.extend({easing:"swing",duration:300},d,e);if(d.toHide.size())if(d.toShow.size()){var f=d.toShow.css("overflow"),g=0,a={},c={},h;e=d.toShow;h=e[0].style.width;e.width(parseInt(e.parent().width(),10)-parseInt(e.css("paddingLeft"),10)-parseInt(e.css("paddingRight"),10)-(parseInt(e.css("borderLeftWidth"),10)||0)-(parseInt(e.css("borderRightWidth"),10)||0));b.each(["height","paddingTop","paddingBottom"],function(i,j){c[j]="hide";i=(""+b.css(d.toShow[0],j)).match(/^([\d+-.]+)(.*)$/); +a[j]={value:i[1],unit:i[2]||"px"}});d.toShow.css({height:0,overflow:"hidden"}).show();d.toHide.filter(":hidden").each(d.complete).end().filter(":visible").animate(c,{step:function(i,j){if(j.prop=="height")g=j.end-j.start===0?0:(j.now-j.start)/(j.end-j.start);d.toShow[0].style[j.prop]=g*a[j.prop].value+a[j.prop].unit},duration:d.duration,easing:d.easing,complete:function(){d.autoHeight||d.toShow.css("height","");d.toShow.css({width:h,overflow:f});d.complete()}})}else d.toHide.animate({height:"hide", +paddingTop:"hide",paddingBottom:"hide"},d);else d.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},d)},bounceslide:function(d){this.slide(d,{easing:d.down?"easeOutBounce":"swing",duration:d.down?1E3:200})}}})})(jQuery); +(function(b){var d=0;b.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var e=this,f=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(a){if(!(e.options.disabled||e.element.attr("readonly"))){g= +false;var c=b.ui.keyCode;switch(a.keyCode){case c.PAGE_UP:e._move("previousPage",a);break;case c.PAGE_DOWN:e._move("nextPage",a);break;case c.UP:e._move("previous",a);a.preventDefault();break;case c.DOWN:e._move("next",a);a.preventDefault();break;case c.ENTER:case c.NUMPAD_ENTER:if(e.menu.active){g=true;a.preventDefault()}case c.TAB:if(!e.menu.active)return;e.menu.select(a);break;case c.ESCAPE:e.element.val(e.term);e.close(a);break;default:clearTimeout(e.searching);e.searching=setTimeout(function(){if(e.term!= +e.element.val()){e.selectedItem=null;e.search(null,a)}},e.options.delay);break}}}).bind("keypress.autocomplete",function(a){if(g){g=false;a.preventDefault()}}).bind("focus.autocomplete",function(){if(!e.options.disabled){e.selectedItem=null;e.previous=e.element.val()}}).bind("blur.autocomplete",function(a){if(!e.options.disabled){clearTimeout(e.searching);e.closing=setTimeout(function(){e.close(a);e._change(a)},150)}});this._initSource();this.response=function(){return e._response.apply(e,arguments)}; +this.menu=b("
        ").addClass("ui-autocomplete").appendTo(b(this.options.appendTo||"body",f)[0]).mousedown(function(a){var c=e.menu.element[0];b(a.target).closest(".ui-menu-item").length||setTimeout(function(){b(document).one("mousedown",function(h){h.target!==e.element[0]&&h.target!==c&&!b.ui.contains(c,h.target)&&e.close()})},1);setTimeout(function(){clearTimeout(e.closing)},13)}).menu({focus:function(a,c){c=c.item.data("item.autocomplete");false!==e._trigger("focus",a,{item:c})&&/^key/.test(a.originalEvent.type)&& +e.element.val(c.value)},selected:function(a,c){var h=c.item.data("item.autocomplete"),i=e.previous;if(e.element[0]!==f.activeElement){e.element.focus();e.previous=i;setTimeout(function(){e.previous=i;e.selectedItem=h},1)}false!==e._trigger("select",a,{item:h})&&e.element.val(h.value);e.term=e.element.val();e.close(a);e.selectedItem=h},blur:function(){e.menu.element.is(":visible")&&e.element.val()!==e.term&&e.element.val(e.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"); +b.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();b.Widget.prototype.destroy.call(this)},_setOption:function(e,f){b.Widget.prototype._setOption.apply(this,arguments);e==="source"&&this._initSource();if(e==="appendTo")this.menu.element.appendTo(b(f||"body",this.element[0].ownerDocument)[0]);e==="disabled"&& +f&&this.xhr&&this.xhr.abort()},_initSource:function(){var e=this,f,g;if(b.isArray(this.options.source)){f=this.options.source;this.source=function(a,c){c(b.ui.autocomplete.filter(f,a.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(a,c){e.xhr&&e.xhr.abort();e.xhr=b.ajax({url:g,data:a,dataType:"json",autocompleteRequest:++d,success:function(h){this.autocompleteRequest===d&&c(h)},error:function(){this.autocompleteRequest===d&&c([])}})}}else this.source= +this.options.source},search:function(e,f){e=e!=null?e:this.element.val();this.term=this.element.val();if(e.length
      • ").data("item.autocomplete",f).append(b("").text(f.label)).appendTo(e)},_move:function(e,f){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(e)||this.menu.last()&&/^next/.test(e)){this.element.val(this.term);this.menu.deactivate()}else this.menu[e](f);else this.search(null,f)},widget:function(){return this.menu.element}});b.extend(b.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, +"\\$&")},filter:function(e,f){var g=new RegExp(b.ui.autocomplete.escapeRegex(f),"i");return b.grep(e,function(a){return g.test(a.label||a.value||a)})}})})(jQuery); +(function(b){b.widget("ui.menu",{_create:function(){var d=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(e){if(b(e.target).closest(".ui-menu-item a").length){e.preventDefault();d.select(e)}});this.refresh()},refresh:function(){var d=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", +-1).mouseenter(function(e){d.activate(e,b(this).parent())}).mouseleave(function(){d.deactivate()})},activate:function(d,e){this.deactivate();if(this.hasScroll()){var f=e.offset().top-this.element.offset().top,g=this.element.attr("scrollTop"),a=this.element.height();if(f<0)this.element.attr("scrollTop",g+f);else f>=a&&this.element.attr("scrollTop",g+f-a+e.height())}this.active=e.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",d,{item:e})}, +deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active=null}},next:function(d){this.move("next",".ui-menu-item:first",d)},previous:function(d){this.move("prev",".ui-menu-item:last",d)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(d,e,f){if(this.active){d=this.active[d+"All"](".ui-menu-item").eq(0); +d.length?this.activate(f,d):this.activate(f,this.element.children(e))}else this.activate(f,this.element.children(e))},nextPage:function(d){if(this.hasScroll())if(!this.active||this.last())this.activate(d,this.element.children(".ui-menu-item:first"));else{var e=this.active.offset().top,f=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var a=b(this).offset().top-e-f+b(this).height();return a<10&&a>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(d, +g)}else this.activate(d,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(d){if(this.hasScroll())if(!this.active||this.first())this.activate(d,this.element.children(".ui-menu-item:last"));else{var e=this.active.offset().top,f=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=b(this).offset().top-e+f-b(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first")); +this.activate(d,result)}else this.activate(d,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(g.empty()).text(),c=this.options.icons,h=c.primary&&c.secondary,i=[];if(c.primary||c.secondary){if(this.options.text)i.push("ui-button-text-icon"+(h?"s":c.primary?"-primary":"-secondary"));c.primary&&g.prepend("");c.secondary&&g.append("");if(!this.options.text){i.push(h?"ui-button-icons-only": +"ui-button-icon-only");this.hasTitle||g.attr("title",a)}}else i.push("ui-button-text-only");g.addClass(i.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(g,a){g==="disabled"&&this.buttons.button("option",g,a);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()}, +destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");b.Widget.prototype.destroy.call(this)}})})(jQuery); +(function(b,d){function e(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass= +"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su", +"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10", +minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};b.extend(this._defaults,this.regional[""]);this.dpDiv=b('
        ')}function f(a,c){b.extend(a,c);for(var h in c)if(c[h]== +null||c[h]==d)a[h]=c[h];return a}b.extend(b.ui,{datepicker:{version:"1.8.12"}});var g=(new Date).getTime();b.extend(e.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){f(this._defaults,a||{});return this},_attachDatepicker:function(a,c){var h=null;for(var i in this._defaults){var j=a.getAttribute("date:"+i);if(j){h=h||{};try{h[i]=eval(j)}catch(n){h[i]=j}}}i=a.nodeName.toLowerCase(); +j=i=="div"||i=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var o=this._newInst(b(a),j);o.settings=b.extend({},c||{},h||{});if(i=="input")this._connectDatepicker(a,o);else j&&this._inlineDatepicker(a,o)},_newInst:function(a,c){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:c,dpDiv:!c?this.dpDiv:b('
        ')}}, +_connectDatepicker:function(a,c){var h=b(a);c.append=b([]);c.trigger=b([]);if(!h.hasClass(this.markerClassName)){this._attachments(h,c);h.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(i,j,n){c.settings[j]=n}).bind("getData.datepicker",function(i,j){return this._get(c,j)});this._autoSize(c);b.data(a,"datepicker",c)}},_attachments:function(a,c){var h=this._get(c,"appendText"),i=this._get(c,"isRTL");c.append&& +c.append.remove();if(h){c.append=b(''+h+"");a[i?"before":"after"](c.append)}a.unbind("focus",this._showDatepicker);c.trigger&&c.trigger.remove();h=this._get(c,"showOn");if(h=="focus"||h=="both")a.focus(this._showDatepicker);if(h=="button"||h=="both"){h=this._get(c,"buttonText");var j=this._get(c,"buttonImage");c.trigger=b(this._get(c,"buttonImageOnly")?b("").addClass(this._triggerClass).attr({src:j,alt:h,title:h}):b('').addClass(this._triggerClass).html(j== +""?h:b("").attr({src:j,alt:h,title:h})));a[i?"before":"after"](c.trigger);c.trigger.click(function(){b.datepicker._datepickerShowing&&b.datepicker._lastInput==a[0]?b.datepicker._hideDatepicker():b.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var c=new Date(2009,11,20),h=this._get(a,"dateFormat");if(h.match(/[DM]/)){var i=function(j){for(var n=0,o=0,l=0;ln){n=j[l].length;o=l}return o};c.setMonth(i(this._get(a, +h.match(/MM/)?"monthNames":"monthNamesShort")));c.setDate(i(this._get(a,h.match(/DD/)?"dayNames":"dayNamesShort"))+20-c.getDay())}a.input.attr("size",this._formatDate(a,c).length)}},_inlineDatepicker:function(a,c){var h=b(a);if(!h.hasClass(this.markerClassName)){h.addClass(this.markerClassName).append(c.dpDiv).bind("setData.datepicker",function(i,j,n){c.settings[j]=n}).bind("getData.datepicker",function(i,j){return this._get(c,j)});b.data(a,"datepicker",c);this._setDate(c,this._getDefaultDate(c), +true);this._updateDatepicker(c);this._updateAlternate(c);c.dpDiv.show()}},_dialogDatepicker:function(a,c,h,i,j){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=b('');this._dialogInput.keydown(this._doKeyDown);b("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};b.data(this._dialogInput[0],"datepicker",a)}f(a.settings,i||{}); +c=c&&c.constructor==Date?this._formatDate(a,c):c;this._dialogInput.val(c);this._pos=j?j.length?j:[j.pageX,j.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=h;this._inDialog=true;this.dpDiv.addClass(this._dialogClass); +this._showDatepicker(this._dialogInput[0]);b.blockUI&&b.blockUI(this.dpDiv);b.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var c=b(a),h=b.data(a,"datepicker");if(c.hasClass(this.markerClassName)){var i=a.nodeName.toLowerCase();b.removeData(a,"datepicker");if(i=="input"){h.append.remove();h.trigger.remove();c.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup", +this._doKeyUp)}else if(i=="div"||i=="span")c.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var c=b(a),h=b.data(a,"datepicker");if(c.hasClass(this.markerClassName)){var i=a.nodeName.toLowerCase();if(i=="input"){a.disabled=false;h.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(i=="div"||i=="span")c.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=b.map(this._disabledInputs, +function(j){return j==a?null:j})}},_disableDatepicker:function(a){var c=b(a),h=b.data(a,"datepicker");if(c.hasClass(this.markerClassName)){var i=a.nodeName.toLowerCase();if(i=="input"){a.disabled=true;h.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(i=="div"||i=="span")c.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=b.map(this._disabledInputs,function(j){return j==a?null: +j});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var c=0;c-1}},_doKeyUp:function(a){a=b.datepicker._getInst(a.target); +if(a.input.val()!=a.lastVal)try{if(b.datepicker.parseDate(b.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,b.datepicker._getFormatConfig(a))){b.datepicker._setDateFromField(a);b.datepicker._updateAlternate(a);b.datepicker._updateDatepicker(a)}}catch(c){b.datepicker.log(c)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=b("input",a.parentNode)[0];if(!(b.datepicker._isDisabledDatepicker(a)||b.datepicker._lastInput==a)){var c=b.datepicker._getInst(a); +b.datepicker._curInst&&b.datepicker._curInst!=c&&b.datepicker._curInst.dpDiv.stop(true,true);var h=b.datepicker._get(c,"beforeShow");f(c.settings,h?h.apply(a,[a,c]):{});c.lastVal=null;b.datepicker._lastInput=a;b.datepicker._setDateFromField(c);if(b.datepicker._inDialog)a.value="";if(!b.datepicker._pos){b.datepicker._pos=b.datepicker._findPos(a);b.datepicker._pos[1]+=a.offsetHeight}var i=false;b(a).parents().each(function(){i|=b(this).css("position")=="fixed";return!i});if(i&&b.browser.opera){b.datepicker._pos[0]-= +document.documentElement.scrollLeft;b.datepicker._pos[1]-=document.documentElement.scrollTop}h={left:b.datepicker._pos[0],top:b.datepicker._pos[1]};b.datepicker._pos=null;c.dpDiv.empty();c.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});b.datepicker._updateDatepicker(c);h=b.datepicker._checkOffset(c,h,i);c.dpDiv.css({position:b.datepicker._inDialog&&b.blockUI?"static":i?"fixed":"absolute",display:"none",left:h.left+"px",top:h.top+"px"});if(!c.inline){h=b.datepicker._get(c,"showAnim"); +var j=b.datepicker._get(c,"duration"),n=function(){b.datepicker._datepickerShowing=true;var o=c.dpDiv.find("iframe.ui-datepicker-cover");if(o.length){var l=b.datepicker._getBorders(c.dpDiv);o.css({left:-l[0],top:-l[1],width:c.dpDiv.outerWidth(),height:c.dpDiv.outerHeight()})}};c.dpDiv.zIndex(b(a).zIndex()+1);b.effects&&b.effects[h]?c.dpDiv.show(h,b.datepicker._get(c,"showOptions"),j,n):c.dpDiv[h||"show"](h?j:null,n);if(!h||!j)n();c.input.is(":visible")&&!c.input.is(":disabled")&&c.input.focus();b.datepicker._curInst= +c}}},_updateDatepicker:function(a){var c=this,h=b.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a));var i=a.dpDiv.find("iframe.ui-datepicker-cover");i.length&&i.css({left:-h[0],top:-h[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){b(this).removeClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&b(this).removeClass("ui-datepicker-prev-hover"); +this.className.indexOf("ui-datepicker-next")!=-1&&b(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!c._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){b(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");b(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&b(this).addClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&b(this).addClass("ui-datepicker-next-hover")}}).end().find("."+ +this._dayOverClass+" a").trigger("mouseover").end();h=this._getNumberOfMonths(a);i=h[1];i>1?a.dpDiv.addClass("ui-datepicker-multi-"+i).css("width",17*i+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(h[0]!=1||h[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==b.datepicker._curInst&&b.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&& +a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var j=a.yearshtml;setTimeout(function(){j===a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);j=a.yearshtml=null},0)}},_getBorders:function(a){var c=function(h){return{thin:1,medium:2,thick:3}[h]||h};return[parseFloat(c(a.css("border-left-width"))),parseFloat(c(a.css("border-top-width")))]},_checkOffset:function(a,c,h){var i=a.dpDiv.outerWidth(),j=a.dpDiv.outerHeight(),n=a.input?a.input.outerWidth(): +0,o=a.input?a.input.outerHeight():0,l=document.documentElement.clientWidth+b(document).scrollLeft(),k=document.documentElement.clientHeight+b(document).scrollTop();c.left-=this._get(a,"isRTL")?i-n:0;c.left-=h&&c.left==a.input.offset().left?b(document).scrollLeft():0;c.top-=h&&c.top==a.input.offset().top+o?b(document).scrollTop():0;c.left-=Math.min(c.left,c.left+i>l&&l>i?Math.abs(c.left+i-l):0);c.top-=Math.min(c.top,c.top+j>k&&k>j?Math.abs(j+o):0);return c},_findPos:function(a){for(var c=this._get(this._getInst(a), +"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||b.expr.filters.hidden(a));)a=a[c?"previousSibling":"nextSibling"];a=b(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var c=this._curInst;if(!(!c||a&&c!=b.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(c,"showAnim");var h=this._get(c,"duration"),i=function(){b.datepicker._tidyDialog(c);this._curInst=null};b.effects&&b.effects[a]?c.dpDiv.hide(a,b.datepicker._get(c,"showOptions"),h,i):c.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"? +"fadeOut":"hide"](a?h:null,i);a||i();if(a=this._get(c,"onClose"))a.apply(c.input?c.input[0]:null,[c.input?c.input.val():"",c]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(b.blockUI){b.unblockUI();b("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(b.datepicker._curInst){a= +b(a.target);a[0].id!=b.datepicker._mainDivId&&a.parents("#"+b.datepicker._mainDivId).length==0&&!a.hasClass(b.datepicker.markerClassName)&&!a.hasClass(b.datepicker._triggerClass)&&b.datepicker._datepickerShowing&&!(b.datepicker._inDialog&&b.blockUI)&&b.datepicker._hideDatepicker()}},_adjustDate:function(a,c,h){a=b(a);var i=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(i,c+(h=="M"?this._get(i,"showCurrentAtPos"):0),h);this._updateDatepicker(i)}},_gotoToday:function(a){a= +b(a);var c=this._getInst(a[0]);if(this._get(c,"gotoCurrent")&&c.currentDay){c.selectedDay=c.currentDay;c.drawMonth=c.selectedMonth=c.currentMonth;c.drawYear=c.selectedYear=c.currentYear}else{var h=new Date;c.selectedDay=h.getDate();c.drawMonth=c.selectedMonth=h.getMonth();c.drawYear=c.selectedYear=h.getFullYear()}this._notifyChange(c);this._adjustDate(a)},_selectMonthYear:function(a,c,h){a=b(a);var i=this._getInst(a[0]);i._selectingMonthYear=false;i["selected"+(h=="M"?"Month":"Year")]=i["draw"+(h== +"M"?"Month":"Year")]=parseInt(c.options[c.selectedIndex].value,10);this._notifyChange(i);this._adjustDate(a)},_clickMonthYear:function(a){var c=this._getInst(b(a)[0]);c.input&&c._selectingMonthYear&&setTimeout(function(){c.input.focus()},0);c._selectingMonthYear=!c._selectingMonthYear},_selectDay:function(a,c,h,i){var j=b(a);if(!(b(i).hasClass(this._unselectableClass)||this._isDisabledDatepicker(j[0]))){j=this._getInst(j[0]);j.selectedDay=j.currentDay=b("a",i).html();j.selectedMonth=j.currentMonth= +c;j.selectedYear=j.currentYear=h;this._selectDate(a,this._formatDate(j,j.currentDay,j.currentMonth,j.currentYear))}},_clearDate:function(a){a=b(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,c){a=this._getInst(b(a)[0]);c=c!=null?c:this._formatDate(a);a.input&&a.input.val(c);this._updateAlternate(a);var h=this._get(a,"onSelect");if(h)h.apply(a.input?a.input[0]:null,[c,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker(); +this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var c=this._get(a,"altField");if(c){var h=this._get(a,"altFormat")||this._get(a,"dateFormat"),i=this._getDate(a),j=this.formatDate(h,i,this._getFormatConfig(a));b(c).each(function(){b(this).val(j)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var c=a.getTime();a.setMonth(0); +a.setDate(1);return Math.floor(Math.round((c-a)/864E5)/7)+1},parseDate:function(a,c,h){if(a==null||c==null)throw"Invalid arguments";c=typeof c=="object"?c.toString():c+"";if(c=="")return null;var i=(h?h.shortYearCutoff:null)||this._defaults.shortYearCutoff;i=typeof i!="string"?i:(new Date).getFullYear()%100+parseInt(i,10);for(var j=(h?h.dayNamesShort:null)||this._defaults.dayNamesShort,n=(h?h.dayNames:null)||this._defaults.dayNames,o=(h?h.monthNamesShort:null)||this._defaults.monthNamesShort,l=(h? +h.monthNames:null)||this._defaults.monthNames,k=h=-1,m=-1,p=-1,q=false,s=function(x){(x=y+1-1){k=1;m=p;do{i=this._getDaysInMonth(h,k-1);if(m<=i)break;k++;m-=i}while(1)}B=this._daylightSavingAdjust(new Date(h,k-1,m));if(B.getFullYear()!=h||B.getMonth()+1!=k||B.getDate()!=m)throw"Invalid date";return B},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y", +RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,c,h){if(!c)return"";var i=(h?h.dayNamesShort:null)||this._defaults.dayNamesShort,j=(h?h.dayNames:null)||this._defaults.dayNames,n=(h?h.monthNamesShort:null)||this._defaults.monthNamesShort;h=(h?h.monthNames:null)||this._defaults.monthNames;var o=function(s){(s=q+112?a.getHours()+2:0);return a},_setDate:function(a,c,h){var i=!c,j=a.selectedMonth,n=a.selectedYear;c=this._restrictMinMax(a,this._determineDate(a,c,new Date));a.selectedDay= +a.currentDay=c.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=c.getMonth();a.drawYear=a.selectedYear=a.currentYear=c.getFullYear();if((j!=a.selectedMonth||n!=a.selectedYear)&&!h)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(i?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var c=new Date;c=this._daylightSavingAdjust(new Date(c.getFullYear(), +c.getMonth(),c.getDate()));var h=this._get(a,"isRTL"),i=this._get(a,"showButtonPanel"),j=this._get(a,"hideIfNoPrevNext"),n=this._get(a,"navigationAsDateFormat"),o=this._getNumberOfMonths(a),l=this._get(a,"showCurrentAtPos"),k=this._get(a,"stepMonths"),m=o[0]!=1||o[1]!=1,p=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),q=this._getMinMaxDate(a,"min"),s=this._getMinMaxDate(a,"max");l=a.drawMonth-l;var r=a.drawYear;if(l<0){l+=12;r--}if(s){var u= +this._daylightSavingAdjust(new Date(s.getFullYear(),s.getMonth()-o[0]*o[1]+1,s.getDate()));for(u=q&&uu;){l--;if(l<0){l=11;r--}}}a.drawMonth=l;a.drawYear=r;u=this._get(a,"prevText");u=!n?u:this.formatDate(u,this._daylightSavingAdjust(new Date(r,l-k,1)),this._getFormatConfig(a));u=this._canAdjustMonth(a,-1,r,l)?''+u+"":j?"":''+u+"";var v=this._get(a,"nextText");v=!n?v:this.formatDate(v,this._daylightSavingAdjust(new Date(r,l+k,1)),this._getFormatConfig(a));j=this._canAdjustMonth(a,+1,r,l)?''+v+"":j?"":''+v+"";k=this._get(a,"currentText");v=this._get(a,"gotoCurrent")&&a.currentDay?p:c;k=!n?k:this.formatDate(k,v,this._getFormatConfig(a));n=!a.inline?'":"";i=i?'
        '+(h?n:"")+(this._isInRange(a,v)?'":"")+(h?"":n)+"
        ":"";n=parseInt(this._get(a,"firstDay"),10);n=isNaN(n)?0:n;k=this._get(a,"showWeek");v=this._get(a,"dayNames");this._get(a,"dayNamesShort");var w=this._get(a,"dayNamesMin"),y= +this._get(a,"monthNames"),B=this._get(a,"monthNamesShort"),x=this._get(a,"beforeShowDay"),C=this._get(a,"showOtherMonths"),J=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var M=this._getDefaultDate(a),K="",G=0;G1)switch(H){case 0:D+=" ui-datepicker-group-first";A=" ui-corner-"+(h?"right":"left");break;case o[1]- +1:D+=" ui-datepicker-group-last";A=" ui-corner-"+(h?"left":"right");break;default:D+=" ui-datepicker-group-middle";A="";break}D+='">'}D+='
        '+(/all|left/.test(A)&&G==0?h?j:u:"")+(/all|right/.test(A)&&G==0?h?u:j:"")+this._generateMonthYearHeader(a,l,r,q,s,G>0||H>0,y,B)+'
        ';var E=k?'":"";for(A=0;A<7;A++){var z= +(A+n)%7;E+="=5?' class="ui-datepicker-week-end"':"")+'>'+w[z]+""}D+=E+"";E=this._getDaysInMonth(r,l);if(r==a.selectedYear&&l==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,E);A=(this._getFirstDayOfMonth(r,l)-n+7)%7;E=m?6:Math.ceil((A+E)/7);z=this._daylightSavingAdjust(new Date(r,l,1-A));for(var P=0;P";var Q=!k?"":'";for(A=0;A<7;A++){var I= +x?x.apply(a.input?a.input[0]:null,[z]):[true,""],F=z.getMonth()!=l,L=F&&!J||!I[0]||q&&zs;Q+='";z.setDate(z.getDate()+1);z=this._daylightSavingAdjust(z)}D+= +Q+""}l++;if(l>11){l=0;r++}D+="
        '+this._get(a,"weekHeader")+"
        '+this._get(a,"calculateWeek")(z)+""+(F&&!C?" ":L?''+z.getDate()+"":''+z.getDate()+"")+"
        "+(m?""+(o[0]>0&&H==o[1]-1?'
        ':""):"");N+=D}K+=N}K+=i+(b.browser.msie&&parseInt(b.browser.version,10)<7&&!a.inline?'':"");a._keyEvent=false;return K},_generateMonthYearHeader:function(a,c,h,i,j,n,o,l){var k=this._get(a,"changeMonth"),m=this._get(a,"changeYear"),p=this._get(a,"showMonthAfterYear"),q='
        ', +s="";if(n||!k)s+=''+o[c]+"";else{o=i&&i.getFullYear()==h;var r=j&&j.getFullYear()==h;s+='"}p||(q+=s+(n||!(k&& +m)?" ":""));if(!a.yearshtml){a.yearshtml="";if(n||!m)q+=''+h+"";else{l=this._get(a,"yearRange").split(":");var v=(new Date).getFullYear();o=function(w){w=w.match(/c[+-].*/)?h+parseInt(w.substring(1),10):w.match(/[+-].*/)?v+parseInt(w,10):parseInt(w,10);return isNaN(w)?v:w};c=o(l[0]);l=Math.max(c,o(l[1]||""));c=i?Math.max(c,i.getFullYear()):c;l=j?Math.min(l,j.getFullYear()):l;for(a.yearshtml+='";if(b.browser.mozilla)q+='";else{q+=a.yearshtml;a.yearshtml=null}}}q+=this._get(a,"yearSuffix");if(p)q+=(n||!(k&&m)?" ":"")+s;q+="
        ";return q},_adjustInstDate:function(a,c,h){var i= +a.drawYear+(h=="Y"?c:0),j=a.drawMonth+(h=="M"?c:0);c=Math.min(a.selectedDay,this._getDaysInMonth(i,j))+(h=="D"?c:0);i=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(i,j,c)));a.selectedDay=i.getDate();a.drawMonth=a.selectedMonth=i.getMonth();a.drawYear=a.selectedYear=i.getFullYear();if(h=="M"||h=="Y")this._notifyChange(a)},_restrictMinMax:function(a,c){var h=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");c=h&&ca?a:c},_notifyChange:function(a){var c=this._get(a, +"onChangeMonthYear");if(c)c.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,c){return this._determineDate(a,this._get(a,c+"Date"),null)},_getDaysInMonth:function(a,c){return 32-this._daylightSavingAdjust(new Date(a,c,32)).getDate()},_getFirstDayOfMonth:function(a,c){return(new Date(a,c,1)).getDay()},_canAdjustMonth:function(a,c,h,i){var j=this._getNumberOfMonths(a); +h=this._daylightSavingAdjust(new Date(h,i+(c<0?c:j[0]*j[1]),1));c<0&&h.setDate(this._getDaysInMonth(h.getFullYear(),h.getMonth()));return this._isInRange(a,h)},_isInRange:function(a,c){var h=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!h||c.getTime()>=h.getTime())&&(!a||c.getTime()<=a.getTime())},_getFormatConfig:function(a){var c=this._get(a,"shortYearCutoff");c=typeof c!="string"?c:(new Date).getFullYear()%100+parseInt(c,10);return{shortYearCutoff:c,dayNamesShort:this._get(a, +"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,c,h,i){if(!c){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}c=c?typeof c=="object"?c:this._daylightSavingAdjust(new Date(i,h,c)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),c,this._getFormatConfig(a))}});b.fn.datepicker= +function(a){if(!this.length)return this;if(!b.datepicker.initialized){b(document).mousedown(b.datepicker._checkExternalClick).find("body").append(b.datepicker.dpDiv);b.datepicker.initialized=true}var c=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return b.datepicker["_"+a+"Datepicker"].apply(b.datepicker,[this[0]].concat(c));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return b.datepicker["_"+a+"Datepicker"].apply(b.datepicker, +[this[0]].concat(c));return this.each(function(){typeof a=="string"?b.datepicker["_"+a+"Datepicker"].apply(b.datepicker,[this].concat(c)):b.datepicker._attachDatepicker(this,a)})};b.datepicker=new e;b.datepicker.initialized=false;b.datepicker.uuid=(new Date).getTime();b.datepicker.version="1.8.12";window["DP_jQuery_"+g]=b})(jQuery); +(function(b,d){var e={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},f={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},g=b.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};b.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false, +position:{my:"center",at:"center",collision:"fit",using:function(a){var c=b(this).css(a).offset().top;c<0&&b(this).css("top",a.top-c)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,c=a.options,h=c.title||" ",i=b.ui.dialog.getTitleId(a.element),j=(a.uiDialog=b("
        ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ +c.dialogClass).css({zIndex:c.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(l){if(c.closeOnEscape&&l.keyCode&&l.keyCode===b.ui.keyCode.ESCAPE){a.close(l);l.preventDefault()}}).attr({role:"dialog","aria-labelledby":i}).mousedown(function(l){a.moveToTop(false,l)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(j);var n=(a.uiDialogTitlebar=b("
        ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(j), +o=b('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){o.addClass("ui-state-hover")},function(){o.removeClass("ui-state-hover")}).focus(function(){o.addClass("ui-state-focus")}).blur(function(){o.removeClass("ui-state-focus")}).click(function(l){a.close(l);return false}).appendTo(n);(a.uiDialogTitlebarCloseText=b("")).addClass("ui-icon ui-icon-closethick").text(c.closeText).appendTo(o);b("").addClass("ui-dialog-title").attr("id", +i).html(h).prependTo(n);if(b.isFunction(c.beforeclose)&&!b.isFunction(c.beforeClose))c.beforeClose=c.beforeclose;n.find("*").add(n).disableSelection();c.draggable&&b.fn.draggable&&a._makeDraggable();c.resizable&&b.fn.resizable&&a._makeResizable();a._createButtons(c.buttons);a._isOpen=false;b.fn.bgiframe&&j.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); +a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var c=this,h,i;if(false!==c._trigger("beforeClose",a)){c.overlay&&c.overlay.destroy();c.uiDialog.unbind("keypress.ui-dialog");c._isOpen=false;if(c.options.hide)c.uiDialog.hide(c.options.hide,function(){c._trigger("close",a)});else{c.uiDialog.hide();c._trigger("close",a)}b.ui.dialog.overlay.resize();if(c.options.modal){h=0;b(".ui-dialog").each(function(){if(this!== +c.uiDialog[0]){i=b(this).css("z-index");isNaN(i)||(h=Math.max(h,i))}});b.ui.dialog.maxZ=h}return c}},isOpen:function(){return this._isOpen},moveToTop:function(a,c){var h=this,i=h.options;if(i.modal&&!a||!i.stack&&!i.modal)return h._trigger("focus",c);if(i.zIndex>b.ui.dialog.maxZ)b.ui.dialog.maxZ=i.zIndex;if(h.overlay){b.ui.dialog.maxZ+=1;h.overlay.$el.css("z-index",b.ui.dialog.overlay.maxZ=b.ui.dialog.maxZ)}a={scrollTop:h.element.attr("scrollTop"),scrollLeft:h.element.attr("scrollLeft")};b.ui.dialog.maxZ+= +1;h.uiDialog.css("z-index",b.ui.dialog.maxZ);h.element.attr(a);h._trigger("focus",c);return h},open:function(){if(!this._isOpen){var a=this,c=a.options,h=a.uiDialog;a.overlay=c.modal?new b.ui.dialog.overlay(a):null;a._size();a._position(c.position);h.show(c.show);a.moveToTop(true);c.modal&&h.bind("keypress.ui-dialog",function(i){if(i.keyCode===b.ui.keyCode.TAB){var j=b(":tabbable",this),n=j.filter(":first");j=j.filter(":last");if(i.target===j[0]&&!i.shiftKey){n.focus(1);return false}else if(i.target=== +n[0]&&i.shiftKey){j.focus(1);return false}}});b(a.element.find(":tabbable").get().concat(h.find(".ui-dialog-buttonpane :tabbable").get().concat(h.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var c=this,h=false,i=b("
        ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),j=b("
        ").addClass("ui-dialog-buttonset").appendTo(i);c.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&b.each(a, +function(){return!(h=true)});if(h){b.each(a,function(n,o){o=b.isFunction(o)?{click:o,text:n}:o;var l=b('').click(function(){o.click.apply(c.element[0],arguments)}).appendTo(j);b.each(o,function(k,m){if(k!=="click")k in g?l[k](m):l.attr(k,m)});b.fn.button&&l.button()});i.appendTo(c.uiDialog)}},_makeDraggable:function(){function a(n){return{position:n.position,offset:n.offset}}var c=this,h=c.options,i=b(document),j;c.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close", +handle:".ui-dialog-titlebar",containment:"document",start:function(n,o){j=h.height==="auto"?"auto":b(this).height();b(this).height(b(this).height()).addClass("ui-dialog-dragging");c._trigger("dragStart",n,a(o))},drag:function(n,o){c._trigger("drag",n,a(o))},stop:function(n,o){h.position=[o.position.left-i.scrollLeft(),o.position.top-i.scrollTop()];b(this).removeClass("ui-dialog-dragging").height(j);c._trigger("dragStop",n,a(o));b.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function c(n){return{originalPosition:n.originalPosition, +originalSize:n.originalSize,position:n.position,size:n.size}}a=a===d?this.options.resizable:a;var h=this,i=h.options,j=h.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";h.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:h.element,maxWidth:i.maxWidth,maxHeight:i.maxHeight,minWidth:i.minWidth,minHeight:h._minHeight(),handles:a,start:function(n,o){b(this).addClass("ui-dialog-resizing");h._trigger("resizeStart",n,c(o))},resize:function(n,o){h._trigger("resize", +n,c(o))},stop:function(n,o){b(this).removeClass("ui-dialog-resizing");i.height=b(this).height();i.width=b(this).width();h._trigger("resizeStop",n,c(o));b.ui.dialog.overlay.resize()}}).css("position",j).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var c=[],h=[0,0],i;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){c=a.split?a.split(" "): +[a[0],a[1]];if(c.length===1)c[1]=c[0];b.each(["left","top"],function(j,n){if(+c[j]===c[j]){h[j]=c[j];c[j]=n}});a={my:c.join(" "),at:c.join(" "),offset:h.join(" ")}}a=b.extend({},b.ui.dialog.prototype.options.position,a)}else a=b.ui.dialog.prototype.options.position;(i=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(b.extend({of:window},a));i||this.uiDialog.hide()},_setOptions:function(a){var c=this,h={},i=false;b.each(a,function(j,n){c._setOption(j,n); +if(j in e)i=true;if(j in f)h[j]=n});i&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",h)},_setOption:function(a,c){var h=this,i=h.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":h._createButtons(c);break;case "closeText":h.uiDialogTitlebarCloseText.text(""+c);break;case "dialogClass":i.removeClass(h.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+c);break;case "disabled":c?i.addClass("ui-dialog-disabled"): +i.removeClass("ui-dialog-disabled");break;case "draggable":var j=i.is(":data(draggable)");j&&!c&&i.draggable("destroy");!j&&c&&h._makeDraggable();break;case "position":h._position(c);break;case "resizable":(j=i.is(":data(resizable)"))&&!c&&i.resizable("destroy");j&&typeof c==="string"&&i.resizable("option","handles",c);!j&&c!==false&&h._makeResizable(c);break;case "title":b(".ui-dialog-title",h.uiDialogTitlebar).html(""+(c||" "));break}b.Widget.prototype._setOption.apply(h,arguments)},_size:function(){var a= +this.options,c,h,i=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;c=this.uiDialog.css({height:"auto",width:a.width}).height();h=Math.max(0,a.minHeight-c);if(a.height==="auto")if(b.support.minHeight)this.element.css({minHeight:h,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();i||this.uiDialog.hide();this.element.height(Math.max(a,h))}else this.element.height(Math.max(a.height- +c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});b.extend(b.ui.dialog,{version:"1.8.12",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=b.ui.dialog.overlay.create(a)}});b.extend(b.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:b.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "), +create:function(a){if(this.instances.length===0){setTimeout(function(){b.ui.dialog.overlay.instances.length&&b(document).bind(b.ui.dialog.overlay.events,function(h){if(b(h.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(), +height:this.height()});b.fn.bgiframe&&c.bgiframe();this.instances.push(c);return c},destroy:function(a){var c=b.inArray(a,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]);this.instances.length===0&&b([document,window]).unbind(".dialog-overlay");a.remove();var h=0;b.each(this.instances,function(){h=Math.max(h,this.css("z-index"))});this.maxZ=h},height:function(){var a,c;if(b.browser.msie&&b.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight); +c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a0?a.left-h:Math.max(a.left-c.collisionPosition.left,a.left)},top:function(a,c){var h=b(window);h=c.collisionPosition.top+c.collisionHeight-h.height()-h.scrollTop();a.top=h>0?a.top-h:Math.max(a.top-c.collisionPosition.top,a.top)}},flip:{left:function(a,c){if(c.at[0]!=="center"){var h=b(window);h=c.collisionPosition.left+c.collisionWidth-h.width()-h.scrollLeft();var i=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,j=c.at[0]==="left"?c.targetWidth:-c.targetWidth,n=-2*c.offset[0];a.left+= +c.collisionPosition.left<0?i+j+n:h>0?i+j+n:0}},top:function(a,c){if(c.at[1]!=="center"){var h=b(window);h=c.collisionPosition.top+c.collisionHeight-h.height()-h.scrollTop();var i=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,j=c.at[1]==="top"?c.targetHeight:-c.targetHeight,n=-2*c.offset[1];a.top+=c.collisionPosition.top<0?i+j+n:h>0?i+j+n:0}}}};if(!b.offset.setOffset){b.offset.setOffset=function(a,c){if(/static/.test(b.curCSS(a,"position")))a.style.position="relative";var h=b(a), +i=h.offset(),j=parseInt(b.curCSS(a,"top",true),10)||0,n=parseInt(b.curCSS(a,"left",true),10)||0;i={top:c.top-i.top+j,left:c.left-i.left+n};"using"in c?c.using.call(a,i):h.css(i)};b.fn.offset=function(a){var c=this[0];if(!c||!c.ownerDocument)return null;if(a)return this.each(function(){b.offset.setOffset(this,a)});return g.call(this)}}})(jQuery); +(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
        ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); +this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(e){if(e===d)return this._value();this._setOption("value",e);return this},_setOption:function(e,f){if(e==="value"){this.options.value=f;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var e=this.options.value;if(typeof e!=="number")e=0;return Math.min(this.options.max,Math.max(this.min,e))},_percentage:function(){return 100* +this._value()/this.options.max},_refreshValue:function(){var e=this.value(),f=this._percentage();if(this.oldValue!==e){this.oldValue=e;this._trigger("change")}this.valueDiv.toggle(e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(f.toFixed(0)+"%");this.element.attr("aria-valuenow",e)}});b.extend(b.ui.progressbar,{version:"1.8.12"})})(jQuery); +(function(b){b.widget("ui.slider",b.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var d=this,e=this.options;this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");e.disabled&&this.element.addClass("ui-slider-disabled ui-disabled"); +this.range=b([]);if(e.range){if(e.range===true){this.range=b("
        ");if(!e.values)e.values=[this._valueMin(),this._valueMin()];if(e.values.length&&e.values.length!==2)e.values=[e.values[0],e.values[0]]}else this.range=b("
        ");this.range.appendTo(this.element).addClass("ui-slider-range");if(e.range==="min"||e.range==="max")this.range.addClass("ui-slider-range-"+e.range);this.range.addClass("ui-widget-header")}b(".ui-slider-handle",this.element).length===0&&b("").appendTo(this.element).addClass("ui-slider-handle"); +if(e.values&&e.values.length)for(;b(".ui-slider-handle",this.element).length").appendTo(this.element).addClass("ui-slider-handle");this.handles=b(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(f){f.preventDefault()}).hover(function(){e.disabled||b(this).addClass("ui-state-hover")},function(){b(this).removeClass("ui-state-hover")}).focus(function(){if(e.disabled)b(this).blur(); +else{b(".ui-slider .ui-state-focus").removeClass("ui-state-focus");b(this).addClass("ui-state-focus")}}).blur(function(){b(this).removeClass("ui-state-focus")});this.handles.each(function(f){b(this).data("index.ui-slider-handle",f)});this.handles.keydown(function(f){var g=true,a=b(this).data("index.ui-slider-handle"),c,h,i;if(!d.options.disabled){switch(f.keyCode){case b.ui.keyCode.HOME:case b.ui.keyCode.END:case b.ui.keyCode.PAGE_UP:case b.ui.keyCode.PAGE_DOWN:case b.ui.keyCode.UP:case b.ui.keyCode.RIGHT:case b.ui.keyCode.DOWN:case b.ui.keyCode.LEFT:g= +false;if(!d._keySliding){d._keySliding=true;b(this).addClass("ui-state-active");c=d._start(f,a);if(c===false)return}break}i=d.options.step;c=d.options.values&&d.options.values.length?(h=d.values(a)):(h=d.value());switch(f.keyCode){case b.ui.keyCode.HOME:h=d._valueMin();break;case b.ui.keyCode.END:h=d._valueMax();break;case b.ui.keyCode.PAGE_UP:h=d._trimAlignValue(c+(d._valueMax()-d._valueMin())/5);break;case b.ui.keyCode.PAGE_DOWN:h=d._trimAlignValue(c-(d._valueMax()-d._valueMin())/5);break;case b.ui.keyCode.UP:case b.ui.keyCode.RIGHT:if(c=== +d._valueMax())return;h=d._trimAlignValue(c+i);break;case b.ui.keyCode.DOWN:case b.ui.keyCode.LEFT:if(c===d._valueMin())return;h=d._trimAlignValue(c-i);break}d._slide(f,a,h);return g}}).keyup(function(f){var g=b(this).data("index.ui-slider-handle");if(d._keySliding){d._keySliding=false;d._stop(f,g);d._change(f,g);b(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"); +this._mouseDestroy();return this},_mouseCapture:function(d){var e=this.options,f,g,a,c,h;if(e.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();f=this._normValueFromMouse({x:d.pageX,y:d.pageY});g=this._valueMax()-this._valueMin()+1;c=this;this.handles.each(function(i){var j=Math.abs(f-c.values(i));if(g>j){g=j;a=b(this);h=i}});if(e.range===true&&this.values(1)===e.min){h+=1;a=b(this.handles[h])}if(this._start(d, +h)===false)return false;this._mouseSliding=true;c._handleIndex=h;a.addClass("ui-state-active").focus();e=a.offset();this._clickOffset=!b(d.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:d.pageX-e.left-a.width()/2,top:d.pageY-e.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(d,h,f);return this._animateOff=true},_mouseStart:function(){return true}, +_mouseDrag:function(d){var e=this._normValueFromMouse({x:d.pageX,y:d.pageY});this._slide(d,this._handleIndex,e);return false},_mouseStop:function(d){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(d,this._handleIndex);this._change(d,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(d){var e; +if(this.orientation==="horizontal"){e=this.elementSize.width;d=d.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{e=this.elementSize.height;d=d.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}e=d/e;if(e>1)e=1;if(e<0)e=0;if(this.orientation==="vertical")e=1-e;d=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+e*d)},_start:function(d,e){var f={handle:this.handles[e],value:this.value()};if(this.options.values&&this.options.values.length){f.value= +this.values(e);f.values=this.values()}return this._trigger("start",d,f)},_slide:function(d,e,f){var g;if(this.options.values&&this.options.values.length){g=this.values(e?0:1);if(this.options.values.length===2&&this.options.range===true&&(e===0&&f>g||e===1&&f1){this.options.values[d]=this._trimAlignValue(e);this._refreshValue();this._change(null,d)}else if(arguments.length)if(b.isArray(arguments[0])){f=this.options.values;g=arguments[0];for(a=0;a=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,f=(d-this._valueMin())%e;alignValue=d-f;if(Math.abs(f)*2>=e)alignValue+=f>0?e:-e;return parseFloat(alignValue.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max}, +_refreshValue:function(){var d=this.options.range,e=this.options,f=this,g=!this._animateOff?e.animate:false,a,c={},h,i,j,n;if(this.options.values&&this.options.values.length)this.handles.each(function(o){a=(f.values(o)-f._valueMin())/(f._valueMax()-f._valueMin())*100;c[f.orientation==="horizontal"?"left":"bottom"]=a+"%";b(this).stop(1,1)[g?"animate":"css"](c,e.animate);if(f.options.range===true)if(f.orientation==="horizontal"){if(o===0)f.range.stop(1,1)[g?"animate":"css"]({left:a+"%"},e.animate); +if(o===1)f.range[g?"animate":"css"]({width:a-h+"%"},{queue:false,duration:e.animate})}else{if(o===0)f.range.stop(1,1)[g?"animate":"css"]({bottom:a+"%"},e.animate);if(o===1)f.range[g?"animate":"css"]({height:a-h+"%"},{queue:false,duration:e.animate})}h=a});else{i=this.value();j=this._valueMin();n=this._valueMax();a=n!==j?(i-j)/(n-j)*100:0;c[f.orientation==="horizontal"?"left":"bottom"]=a+"%";this.handle.stop(1,1)[g?"animate":"css"](c,e.animate);if(d==="min"&&this.orientation==="horizontal")this.range.stop(1, +1)[g?"animate":"css"]({width:a+"%"},e.animate);if(d==="max"&&this.orientation==="horizontal")this.range[g?"animate":"css"]({width:100-a+"%"},{queue:false,duration:e.animate});if(d==="min"&&this.orientation==="vertical")this.range.stop(1,1)[g?"animate":"css"]({height:a+"%"},e.animate);if(d==="max"&&this.orientation==="vertical")this.range[g?"animate":"css"]({height:100-a+"%"},{queue:false,duration:e.animate})}}});b.extend(b.ui.slider,{version:"1.8.12"})})(jQuery); +(function(b,d){function e(){return++g}function f(){return++a}var g=0,a=0;b.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
        ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
      • #{label}
      • "},_create:function(){this._tabify(true)},_setOption:function(c,h){if(c=="selected")this.options.collapsible&& +h==this.options.selected||this.select(h);else{this.options[c]=h;this._tabify()}},_tabId:function(c){return c.title&&c.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+e()},_sanitizeSelector:function(c){return c.replace(/:/g,"\\:")},_cookie:function(){var c=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+f());return b.cookie.apply(null,[c].concat(b.makeArray(arguments)))},_ui:function(c,h){return{tab:c,panel:h,index:this.anchors.index(c)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var c= +b(this);c.html(c.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function h(r,u){r.css("display","");!b.support.opacity&&u.opacity&&r[0].style.removeAttribute("filter")}var i=this,j=this.options,n=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=b(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return b("a",this)[0]});this.panels=b([]);this.anchors.each(function(r,u){var v=b(u).attr("href"),w=v.split("#")[0],y;if(w&&(w===location.toString().split("#")[0]|| +(y=b("base")[0])&&w===y.href)){v=u.hash;u.href=v}if(n.test(v))i.panels=i.panels.add(i.element.find(i._sanitizeSelector(v)));else if(v&&v!=="#"){b.data(u,"href.tabs",v);b.data(u,"load.tabs",v.replace(/#.*$/,""));v=i._tabId(u);u.href="#"+v;u=i.element.find("#"+v);if(!u.length){u=b(j.panelTemplate).attr("id",v).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(i.panels[r-1]||i.list);u.data("destroy.tabs",true)}i.panels=i.panels.add(u)}else j.disabled.push(r)});if(c){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); +this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(j.selected===d){location.hash&&this.anchors.each(function(r,u){if(u.hash==location.hash){j.selected=r;return false}});if(typeof j.selected!=="number"&&j.cookie)j.selected=parseInt(i._cookie(),10);if(typeof j.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)j.selected= +this.lis.index(this.lis.filter(".ui-tabs-selected"));j.selected=j.selected||(this.lis.length?0:-1)}else if(j.selected===null)j.selected=-1;j.selected=j.selected>=0&&this.anchors[j.selected]||j.selected<0?j.selected:0;j.disabled=b.unique(j.disabled.concat(b.map(this.lis.filter(".ui-state-disabled"),function(r){return i.lis.index(r)}))).sort();b.inArray(j.selected,j.disabled)!=-1&&j.disabled.splice(b.inArray(j.selected,j.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); +if(j.selected>=0&&this.anchors.length){i.element.find(i._sanitizeSelector(i.anchors[j.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(j.selected).addClass("ui-tabs-selected ui-state-active");i.element.queue("tabs",function(){i._trigger("show",null,i._ui(i.anchors[j.selected],i.element.find(i._sanitizeSelector(i.anchors[j.selected].hash))[0]))});this.load(j.selected)}b(window).bind("unload",function(){i.lis.add(i.anchors).unbind(".tabs");i.lis=i.anchors=i.panels=null})}else j.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); +this.element[j.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");j.cookie&&this._cookie(j.selected,j.cookie);c=0;for(var o;o=this.lis[c];c++)b(o)[b.inArray(c,j.disabled)!=-1&&!b(o).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");j.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(j.event!=="mouseover"){var l=function(r,u){u.is(":not(.ui-state-disabled)")&&u.addClass("ui-state-"+r)},k=function(r,u){u.removeClass("ui-state-"+ +r)};this.lis.bind("mouseover.tabs",function(){l("hover",b(this))});this.lis.bind("mouseout.tabs",function(){k("hover",b(this))});this.anchors.bind("focus.tabs",function(){l("focus",b(this).closest("li"))});this.anchors.bind("blur.tabs",function(){k("focus",b(this).closest("li"))})}var m,p;if(j.fx)if(b.isArray(j.fx)){m=j.fx[0];p=j.fx[1]}else m=p=j.fx;var q=p?function(r,u){b(r).closest("li").addClass("ui-tabs-selected ui-state-active");u.hide().removeClass("ui-tabs-hide").animate(p,p.duration||"normal", +function(){h(u,p);i._trigger("show",null,i._ui(r,u[0]))})}:function(r,u){b(r).closest("li").addClass("ui-tabs-selected ui-state-active");u.removeClass("ui-tabs-hide");i._trigger("show",null,i._ui(r,u[0]))},s=m?function(r,u){u.animate(m,m.duration||"normal",function(){i.lis.removeClass("ui-tabs-selected ui-state-active");u.addClass("ui-tabs-hide");h(u,m);i.element.dequeue("tabs")})}:function(r,u){i.lis.removeClass("ui-tabs-selected ui-state-active");u.addClass("ui-tabs-hide");i.element.dequeue("tabs")}; +this.anchors.bind(j.event+".tabs",function(){var r=this,u=b(r).closest("li"),v=i.panels.filter(":not(.ui-tabs-hide)"),w=i.element.find(i._sanitizeSelector(r.hash));if(u.hasClass("ui-tabs-selected")&&!j.collapsible||u.hasClass("ui-state-disabled")||u.hasClass("ui-state-processing")||i.panels.filter(":animated").length||i._trigger("select",null,i._ui(this,w[0]))===false){this.blur();return false}j.selected=i.anchors.index(this);i.abort();if(j.collapsible)if(u.hasClass("ui-tabs-selected")){j.selected= +-1;j.cookie&&i._cookie(j.selected,j.cookie);i.element.queue("tabs",function(){s(r,v)}).dequeue("tabs");this.blur();return false}else if(!v.length){j.cookie&&i._cookie(j.selected,j.cookie);i.element.queue("tabs",function(){q(r,w)});i.load(i.anchors.index(this));this.blur();return false}j.cookie&&i._cookie(j.selected,j.cookie);if(w.length){v.length&&i.element.queue("tabs",function(){s(r,v)});i.element.queue("tabs",function(){q(r,w)});i.load(i.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; +b.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(c){if(typeof c=="string")c=this.anchors.index(this.anchors.filter("[href$="+c+"]"));return c},destroy:function(){var c=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var h= +b.data(this,"href.tabs");if(h)this.href=h;var i=b(this).unbind(".tabs");b.each(["href","load","cache"],function(j,n){i.removeData(n+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){b.data(this,"destroy.tabs")?b(this).remove():b(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});c.cookie&&this._cookie(null,c.cookie);return this},add:function(c, +h,i){if(i===d)i=this.anchors.length;var j=this,n=this.options;h=b(n.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,h));c=!c.indexOf("#")?c.replace("#",""):this._tabId(b("a",h)[0]);h.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var o=j.element.find("#"+c);o.length||(o=b(n.panelTemplate).attr("id",c).data("destroy.tabs",true));o.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(i>=this.lis.length){h.appendTo(this.list);o.appendTo(this.list[0].parentNode)}else{h.insertBefore(this.lis[i]); +o.insertBefore(this.panels[i])}n.disabled=b.map(n.disabled,function(l){return l>=i?++l:l});this._tabify();if(this.anchors.length==1){n.selected=0;h.addClass("ui-tabs-selected ui-state-active");o.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){j._trigger("show",null,j._ui(j.anchors[0],j.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[i],this.panels[i]));return this},remove:function(c){c=this._getIndex(c);var h=this.options,i=this.lis.eq(c).remove(),j=this.panels.eq(c).remove(); +if(i.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(c+(c+1=c?--n:n});this._tabify();this._trigger("remove",null,this._ui(i.find("a")[0],j[0]));return this},enable:function(c){c=this._getIndex(c);var h=this.options;if(b.inArray(c,h.disabled)!=-1){this.lis.eq(c).removeClass("ui-state-disabled");h.disabled=b.grep(h.disabled,function(i){return i!=c});this._trigger("enable",null, +this._ui(this.anchors[c],this.panels[c]));return this}},disable:function(c){c=this._getIndex(c);var h=this.options;if(c!=h.selected){this.lis.eq(c).addClass("ui-state-disabled");h.disabled.push(c);h.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[c],this.panels[c]))}return this},select:function(c){c=this._getIndex(c);if(c==-1)if(this.options.collapsible&&this.options.selected!=-1)c=this.options.selected;else return this;this.anchors.eq(c).trigger(this.options.event+".tabs");return this}, +load:function(c){c=this._getIndex(c);var h=this,i=this.options,j=this.anchors.eq(c)[0],n=b.data(j,"load.tabs");this.abort();if(!n||this.element.queue("tabs").length!==0&&b.data(j,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(c).addClass("ui-state-processing");if(i.spinner){var o=b("span",j);o.data("label.tabs",o.html()).html(i.spinner)}this.xhr=b.ajax(b.extend({},i.ajaxOptions,{url:n,success:function(l,k){h.element.find(h._sanitizeSelector(j.hash)).html(l);h._cleanup();i.cache&&b.data(j, +"cache.tabs",true);h._trigger("load",null,h._ui(h.anchors[c],h.panels[c]));try{i.ajaxOptions.success(l,k)}catch(m){}},error:function(l,k){h._cleanup();h._trigger("load",null,h._ui(h.anchors[c],h.panels[c]));try{i.ajaxOptions.error(l,k,c,j)}catch(m){}}}));h.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, +url:function(c,h){this.anchors.eq(c).removeData("cache.tabs").data("load.tabs",h);return this},length:function(){return this.anchors.length}});b.extend(b.ui.tabs,{version:"1.8.12"});b.extend(b.ui.tabs.prototype,{rotation:null,rotate:function(c,h){var i=this,j=this.options,n=i._rotate||(i._rotate=function(o){clearTimeout(i.rotation);i.rotation=setTimeout(function(){var l=j.selected;i.select(++l")}function p(a,b){b=b===m?n.width():n.height();return typeof a==="string"?Math.round(/%/.test(a)?b/100*parseInt(a,10):parseInt(a,10)):a}function U(b){return a.photo||/\.(gif|png|jpg|jpeg|bmp)(?:\?([^#]*))?(?:#(\.*))?$/i.test(b)}function cb(a){for(var c in a)if(b.isFunction(a[c])&&c.substring(0,2)!=="on")a[c]=a[c].call(l);a.rel=a.rel||l.rel||L;a.href=a.href||b(l).attr("href");a.title=a.title||l.title;return a}function w(c,a){a&&a.call(l);b.event.trigger(c)}function jb(){var b,e=i+"Slideshow_",c="click."+i,f,k;if(a.slideshow&&h[1]){f=function(){F.text(a.slideshowStop).unbind(c).bind(V,function(){if(g");c.open=e}a.each(function(){b.data(this,r,b.extend({},b.data(this,r)||gb,c));b(this).addClass(G)});d=c.open;if(b.isFunction(d))d=d.call(a);d&&db(a[0]);return a};d.init=function(){var l="hover",m="clear:left";n=b(ib);j=f().attr({id:r,"class":E?i+"IE":""});x=f("Overlay",X?"position:absolute":"").hide();A=f("Wrapper");s=f("Content").append(k=f(M,"width:0; height:0; overflow:hidden"),K=f("LoadingOverlay").add(f("LoadingGraphic")),Z=f("Title"),Y=f("Current"),I=f("Next"),H=f("Previous"),F=f("Slideshow").bind(fb,jb),ab=f("Close"));A.append(f().append(f("TopLeft"),bb=f("TopCenter"),f("TopRight")),f(c,m).append(T=f("MiddleLeft"),s,R=f("MiddleRight")),f(c,m).append(f("BottomLeft"),S=f("BottomCenter"),f("BottomRight"))).children().children().css({"float":"left"});J=f(c,"position:absolute; width:9999px; visibility:hidden; display:none");b("body").prepend(x,j.append(A,J));s.children().hover(function(){b(this).addClass(l)},function(){b(this).removeClass(l)}).addClass(l);B=bb.height()+S.height()+s.outerHeight(e)-s.height();C=T.width()+R.width()+s.outerWidth(e)-s.width();y=k.outerHeight(e);z=k.outerWidth(e);j.css({"padding-bottom":B,"padding-right":C}).hide();I.click(d.next);H.click(d.prev);ab.click(d.close);s.children().removeClass(l);b("."+G).live("click",function(a){if(!(a.button!==0&&typeof a.button!=="undefined"||a.ctrlKey||a.shiftKey||a.altKey)){a.preventDefault();db(this)}});x.click(function(){a.overlayClose&&d.close()});b(document).bind("keydown",function(b){if(u&&a.escKey&&b.keyCode===27){b.preventDefault();d.close()}if(u&&a.arrowKey&&!D&&h[1])if(b.keyCode===37&&(g||a.loop)){b.preventDefault();H.click()}else if(b.keyCode===39&&(g").children();a.h=b.height();b.replaceWith(b.children())}k.css({height:a.h});d.position(a.transition===t?0:a.speed)}};d.prep=function(m){var c="hidden";function l(s){var p,f,m,c,l=h.length,q=a.loop;d.position(s,function(){function s(){E&&j[0].style.removeAttribute("filter")}if(u){E&&o&&k.fadeIn(100);k.show();w(hb);Z.show().html(a.title);if(l>1){typeof a.current==="string"&&Y.html(a.current.replace(/\{current\}/,g+1).replace(/\{total\}/,l)).show();I[q||g")[0].src=c;if(U(f))b("")[0].src=f}}K.hide();a.transition==="fade"?j.fadeTo(e,1,function(){s()}):s();n.bind(v+i,function(){d.position(0)});w(V,a.onComplete)}})}if(u){var o,e=a.transition===t?0:a.speed;n.unbind(v+i);k.remove();k=f(M).html(m);k.hide().appendTo(J.show()).css({width:function(){a.w=a.w||k.width();a.w=a.mw&&a.mw")[0];c.name=i+ +new Date;c.src=a.href;if(!a.scrolling)c.scrolling="no";if(E)c.allowtransparency="true";b(c).appendTo(k).one(Q,function(){c.src="//about:blank"})});q(" ")}else if(a.html)q(a.html);else if(U(n)){c=new Image;c.onload=function(){var e;c.onload=null;c.id=i+"Photo";b(c).css({border:t,display:"block",cssFloat:"left"});if(a.scalePhotos){s=function(){c.height-=c.height*e;c.width-=c.width*e};if(a.mw&&c.width>a.mw){e=(c.width-a.mw)/c.width;s()}if(a.mh&&c.height>a.mh){e=(c.height-a.mh)/c.height;s()}}if(a.h)c.style.marginTop=Math.max(a.h-c.height,0)/2+"px";h[1]&&(g').append('
        '); + $helper.find('.inside_helper').append($.fn.drag_drop_multi_select.getSelectedForDragging( $clicked_item.attr('ddms'))); + $.fn.drag_drop_multi_select.getListItems(instance_id).filter($.fn.drag_drop_multi_select.settings[instance_id].selectedClass).addClass($.fn.drag_drop_multi_select.settings[instance_id].ghostClass); + return $helper; + }, + cursor:'move', + revert:'invalid', + scroll: false, + start:function(e,ui){ + dragging = true; + }, + drag:function(e,ui){ + + }, + stop:function(e,ui){ + $clicked_item = $(this); + var instance_id = $.fn.drag_drop_multi_select.get_instance_id($clicked_item.attr('ddms')); + $.fn.drag_drop_multi_select.getListItems(instance_id).filter($.fn.drag_drop_multi_select.settings[instance_id].selectedClass).removeClass($.fn.drag_drop_multi_select.settings[instance_id].ghostClass); + } + }); + + // MAKE ITEMS DROPPABLE + var array_of_selected = []; + var found = false; + $(config.elements_to_drop).droppable({ + tolerance:'pointer', + over: function(e,ui) { + var $target = $(e.target); + var old_instance = parseInt(ui.helper.attr('ddms_instance')); + found = false; + array_of_selected = []; + + $.fn.drag_drop_multi_select.getListItems(old_instance).filter(config.selectedClass).each(function(){ + array_of_selected.push($(this).attr('ddms')); + }); + + if($.inArray($target.attr('ddms'),array_of_selected) > -1) { + found = true; + } + + $target.addClass($.fn.drag_drop_multi_select.settings[unique_id].hoverClass); + }, + out: function(e,ui) { + var $target = $(e.target); + $target.removeClass($.fn.drag_drop_multi_select.settings[unique_id].hoverClass); + }, + drop:function(e,ui){ + var $target = $(e.target); + $target.removeClass($.fn.drag_drop_multi_select.settings[unique_id].hoverClass); + + var old_instance = parseInt(ui.helper.attr('ddms_instance')); + + ui.helper.find('.inside_helper .'+config.selectedClass).each(function(){ + var item_id = parseInt( $(this).attr('ddms_drag')); + var $item_instance = $.fn.drag_drop_multi_select.settings['item_'+item_id].self; + $.fn.drag_drop_multi_select.moveBetweenLists( item_id, $item_instance, old_instance, unique_id, e, ui); + }); + } + }); + }); + }; + + // Helper function to clone items after the fact. Useful for shopping cart-like entries. + $.fn.drag_drop_multi_select_clone=function(item) { + var items = item.clone(); + + var self = item.first().closest(".ddms-container"); + var unique_id = self.attr('ddms'); + var config = $.fn.drag_drop_multi_select.settings[unique_id]; + + items.each(function(){ + var $item = $(this); + var item_id = $.fn.drag_drop_multi_select.unique++; + + $item.attr('ddms',item_id); + + $.fn.drag_drop_multi_select.settings['item_'+$item.attr('ddms')] = {self:$item,parent_to_refer:$self}; + $.fn.drag_drop_multi_select.stack[unique_id].all.push(item_id); + + // BIND CLICK ITEM + $item.bind('click.ddms_item_clicked',function(){ + if($.fn.keyisPressed(CTRL_KEY)|| $.fn.keyisPressed(SHIFT_KEY)||($.fn.drag_drop_multi_select.stack[$.fn.drag_drop_multi_select.get_instance_id($(this).attr('ddms'))].selected.length == 1 && $item.hasClass($.fn.drag_drop_multi_select.settings[$.fn.drag_drop_multi_select.get_instance_id($item.attr('ddms'))].selectedClass))){ + $.fn.drag_drop_multi_select.toggle(item_id);//CTRLKEY IS DOWN - ADD ITEM TO SELECTION + }else{ + $.fn.drag_drop_multi_select.replace(item_id);//CTRLKEY IS NOT DOWN - MAKE NEW SELECTION + } + }); + + $item.bind('ddms.select',function(){ + $(this).addClass('ui-selected'); + $(this).addClass($.fn.drag_drop_multi_select.settings[$.fn.drag_drop_multi_select.get_instance_id($(this).attr('ddms'))].selectedClass); + if($self.find('.ui-selected').size() == 0) + $(config.element_to_show_result).text(''); + else + $(config.element_to_show_result).text($self.find('.ui-selected').size()+" items are selected"); + }); + + $item.bind('ddms.deselect',function(){ + $(this).removeClass('ui-selected'); + $(this).removeClass($.fn.drag_drop_multi_select.settings[$.fn.drag_drop_multi_select.get_instance_id($(this).attr('ddms'))].selectedClass); + if($self.find('.ui-selected').size() == 0) + $(config.element_to_show_result).text(''); + else + $(config.element_to_show_result).text($self.find('.ui-selected').size()+" items are selected"); + }); + }); + + // MAKE ITEMS DRAGGABLE + items.draggable({ + helper:function(){ + $clicked_item = $(this); + if(!$clicked_item.hasClass('ddms_selected') ){ + //TRIGGER THE CLICK + $clicked_item.trigger('click.ddms_item_clicked'); + } + // CREATE CLONE HELPER + var instance_id = $.fn.drag_drop_multi_select.get_instance_id($clicked_item.attr('ddms')); + var $helper = $('
        ').append('
        '); + $helper.find('.inside_helper').append($.fn.drag_drop_multi_select.getSelectedForDragging( $clicked_item.attr('ddms'))); + $.fn.drag_drop_multi_select.getListItems(instance_id).filter($.fn.drag_drop_multi_select.settings[instance_id].selectedClass).addClass($.fn.drag_drop_multi_select.settings[instance_id].ghostClass); + return $helper; + }, + cursor:'move', + revert:'invalid', + scroll: false, + start:function(e,ui){ + dragging = true; + }, + drag:function(e,ui){ + + }, + stop:function(e,ui){ + $clicked_item = $(this); + var instance_id = $.fn.drag_drop_multi_select.get_instance_id($clicked_item.attr('ddms')); + $.fn.drag_drop_multi_select.getListItems(instance_id).filter($.fn.drag_drop_multi_select.settings[instance_id].selectedClass).removeClass($.fn.drag_drop_multi_select.settings[instance_id].ghostClass); + } + }); + + return items; + }; + + // DEFAULTS SETTINGS OF PLUGINS + $.fn.drag_drop_multi_select.defaults={ + element_to_drag_drop_select:'.ddms', // element that will be draggable,selectable,droppable + elements_to_cancel_select:'.cancel',// element that you dont want to do selectable + elements_to_drop:'.drop',// element that you can drop on it + element_to_show_result:'.items_selected', // element that will be showing how much are selected + moveOpacity: 0.5, //opacity of moving items + ghostClass: 'ddms_ghost', //class for "left-behind" item. + hoverClass: 'ddms_hover', //class for acceptable drop targets on hover + moveClass: 'ddms_move', //class to apply to items when moving them. + selectedClass: 'ddms_selected', // class to apply to items that will be select. + after_drop_action:function($item_instance,$old_container,$new_container,event,helper){} + }; + $.fn.drag_drop_multi_select.unique=0; + $.fn.drag_drop_multi_select.stack=[]; + $.fn.drag_drop_multi_select.settings=[]; + + // FUNCTION TO EXECUTE AFTER A DROP + $.fn.drag_drop_multi_select.moveBetweenLists=function(item_id, $item_instance, old_container, new_container, event, helper){ + var instance_id = $.fn.drag_drop_multi_select.get_instance_id(item_id); + // DESELECT ITEMS + $.fn.drag_drop_multi_select.deselect(parseInt(item_id)); + $($.fn.drag_drop_multi_select.settings[instance_id].element_to_show_result).text(''); + // DECLAIRE VARIABLES TO SEND TO AFTER DROP FUNCTION + var $old_container = $('[ddms='+old_container+']'); + var $new_container = $('[ddms='+new_container+']'); + // CALL THE AFTER DROP ACTION FUNCTION + $.fn.drag_drop_multi_select.settings[instance_id].after_drop_action($item_instance,$old_container,$new_container,event,helper); + //REMOVING FROM OLD CONTAINER STACK + $.fn.drag_drop_multi_select.stack[old_container].all.splice( $.inArray( parseInt(item_id),$.fn.drag_drop_multi_select.stack[old_container].all ),1); + //ADDING TO THE NEW CONTAINER STACK + $.fn.drag_drop_multi_select.stack[new_container].all.push(parseInt(item_id)); + }; + + // GET CLONE AND ALL ITEM THAT WILL BE DRAG + $.fn.drag_drop_multi_select.getSelectedForDragging=function(item_id){ + var instance_id = $.fn.drag_drop_multi_select.get_instance_id(item_id); + var $clone_list = $.fn.drag_drop_multi_select.getListItems(instance_id).clone().each(function(){ + var id = $(this).attr('ddms'); + $(this).not("."+$.fn.drag_drop_multi_select.settings[instance_id].selectedClass).hide(); + $(this).filter("."+$.fn.drag_drop_multi_select.settings[instance_id].selectedClass).addClass($.fn.drag_drop_multi_select.settings[instance_id].moveClass).css({opacity:$.fn.drag_drop_multi_select.settings[instance_id].moveOpacity}); + $(this).attr('ddms_drag',$(this).attr('ddms')); + $(this).attr('ddms',''); + $(this).css({'position':'absolute','left':$('[ddms="'+id+'"]').position().left,'top':$('[ddms="'+id+'"]').position().top}); + }); + return $clone_list; + }; + + // GET TOP OFFSET OF ITEMS CLIKED + $.fn.drag_drop_multi_select.getTopOffset=function($item){ + //find this items offset and the first items offset. + var this_offset = $item.position().top; + var first_offset = $.fn.drag_drop_multi_select.getListItems($.fn.drag_drop_multi_select.get_instance_id($item.attr('ddms'))).eq(0).position().top; + return this_offset; + }; + + // GET LEFT OFFSET OF ITEMS CLIKED + $.fn.drag_drop_multi_select.getLeftOffset=function($item){ + //find this items offset and the first items offset. + var this_offset = $item.position().left; + var first_offset = $.fn.drag_drop_multi_select.getListItems($.fn.drag_drop_multi_select.get_instance_id($item.attr('ddms'))).eq(0).position().left; + return this_offset; + }; + + // TOGGLE THE SELECT AND DESELECT FUNCTIONS + $.fn.drag_drop_multi_select.toggle=function(item_id){ + if(!$.fn.drag_drop_multi_select.isSelected(item_id)){ + $.fn.drag_drop_multi_select.select(item_id); + }else { + $.fn.drag_drop_multi_select.deselect(item_id); + } + }; + + // FUNCTION THAT SELECT ELEMENT IF ITS NOT ALREADY SELECTED AND ADD IT TO THE STACK OF SELECTED + $.fn.drag_drop_multi_select.select=function(item_id){ + var instance_id = $.fn.drag_drop_multi_select.get_instance_id(item_id); + var $item_instance = $.fn.drag_drop_multi_select.settings['item_'+item_id].self; + + if(!$.fn.drag_drop_multi_select.isSelected(item_id)){ + $.fn.drag_drop_multi_select.stack[instance_id].selected.push(item_id); + $item_instance.trigger('ddms.select'); + } + }; + + // FUNCTION THAT DESELECT ELEMENT FROM STACK + $.fn.drag_drop_multi_select.deselect=function(item_id){ + var instance_id = $.fn.drag_drop_multi_select.get_instance_id(item_id); + var $item_instance = $.fn.drag_drop_multi_select.settings['item_'+item_id].self; + + if($.fn.drag_drop_multi_select.isSelected(item_id)){ + $.fn.drag_drop_multi_select.stack[instance_id].selected.splice($.inArray(item_id,$.fn.drag_drop_multi_select.stack[instance_id].selected),1); + $item_instance.trigger('ddms.deselect'); + } + }; + + // FUNCTION THAT CHECK IF ELEMENT IS ALREADY IN STACK + $.fn.drag_drop_multi_select.isSelected=function(item_id){ + var $item_instance = $.fn.drag_drop_multi_select.settings['item_'+item_id].self; + var instance_id = $.fn.drag_drop_multi_select.get_instance_id(item_id); + return $item_instance.hasClass($.fn.drag_drop_multi_select.settings[instance_id].selectedClass); + }; + + // FUNCTION THAT EMPTY THE STACK AND START A NEW ONE + $.fn.drag_drop_multi_select.replace=function(item_id){ + var instance_id = $.fn.drag_drop_multi_select.get_instance_id(item_id); + $.fn.drag_drop_multi_select.selectNone(instance_id); + $.fn.drag_drop_multi_select.stack[instance_id].selected.push(item_id); + var $item_instance = $.fn.drag_drop_multi_select.settings['item_'+item_id].self; + $item_instance.trigger('ddms.select'); + }; + + // FUNCTION THAT GET ALL ELEMENT THAT ARE IN A STACK + $.fn.drag_drop_multi_select.getListItems=function(instance_id){ + var element_to_drag_drop_select = $.fn.drag_drop_multi_select.settings[instance_id].element_to_drag_drop_select; + return $('[ddms='+instance_id+'] '+element_to_drag_drop_select); + }; + + // FUNCTION THAT RETURN INSTANCE ID OF THE SELECTED ITEM + $.fn.drag_drop_multi_select.get_instance_id=function(item_id){ + return parseInt($.fn.drag_drop_multi_select.settings['item_'+item_id].parent_to_refer.eq(0).attr('ddms')); + }; + + // FUNCTION THAT SERIALIZE AN ARRAY FROM THE RESULTS + $.fn.drag_drop_multi_select.serializeArray=function( list_id ){ + var out = []; + $.fn.drag_drop_multi_select.getListItems(list_id).each(function(){ + out.push($(this).attr('id')); + }); + return out; + }; + $.fn.drag_drop_multi_select.serialize=function( list_id ){ + return $.fn.drag_drop_multi_select.serializeArray( list_id ).join(", "); + }; + + // FUNCTION THAT DESELECT ALL ELEMENT IN A STACK + $.fn.drag_drop_multi_select.selectNone=function(instance_id){ + $.fn.drag_drop_multi_select.getListItems(instance_id).each(function(){ + $.fn.drag_drop_multi_select.deselect( $(this).attr('ddms') ); + }); + return false; + }; + + // FUNCTION THAT SELECT ALL ELEMENT IN A STACK + $.fn.drag_drop_multi_select.selectAll=function(list_id){ + $.fn.drag_drop_multi_select.getListItems(list_id).each(function(){ + $.fn.drag_drop_multi_select.select( $(this).attr('ddms') ); + }); + return false; + }; + + // EXTEND TO ELEMENT SOME FUNCTION + $.extend({ + ddms:{ + selectAll:function(id){ return $.fn.drag_drop_multi_select.selectAll($('#'+id).attr('ddms')); }, + selectNone:function(id){ return $.fn.drag_drop_multi_select.selectNone($('#'+id).attr('ddms')); }, + serialize:function(id){ return $.fn.drag_drop_multi_select.serialize($('#'+id).attr('ddms')); } + } + }); + + // KEY CAPTURING DEFAULT KEYCODE + var CTRL_KEY = 17; + var ALT_KEY = 18; + var SHIFT_KEY = 16; + + // FUNCTION THAT CAPTURE KEY AND CHECK WHAT IS DOWN + $.fn.captureKeys=function(){ + if($.fn.captureKeys.capturing){ return; } + $(document).keydown(function(e){ + if(e.keyCode == CTRL_KEY ){$.fn.captureKeys.stack.CTRL_KEY = true} + if(e.keyCode == SHIFT_KEY){$.fn.captureKeys.stack.SHIFT_KEY = true} + if(e.keyCode == ALT_KEY ){$.fn.captureKeys.stack.ALT_KEY = true} + }); + $(document).keyup(function(e){ + if(e.keyCode == CTRL_KEY ){$.fn.captureKeys.stack.CTRL_KEY = false} + if(e.keyCode == SHIFT_KEY){$.fn.captureKeys.stack.SHIFT_KEY = false} + if(e.keyCode == ALT_KEY ){$.fn.captureKeys.stack.ALT_KEY = false} + }); + + }; + $.fn.captureKeys.stack={CTRL_KEY:false, SHIFT_KEY:false, ALT_KEY:false}; + $.fn.captureKeys.capturing=false; + $.fn.keyisPressed=function(key){ + switch(key){ + case CTRL_KEY: return $.fn.captureKeys.stack.CTRL_KEY; + case ALT_KEY: return $.fn.captureKeys.stack.ALT_KEY; + case SHIFT_KEY: return $.fn.captureKeys.stack.SHIFT_KEY; + default: return false; + } + } +})(jQuery); + diff --git a/crowbar/change-image/dell/openstack_manager/public/javascripts/jquery.js b/crowbar/change-image/dell/openstack_manager/public/javascripts/jquery.js new file mode 100644 index 00000000000..ae0234d2bb4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/public/javascripts/jquery.js @@ -0,0 +1,8374 @@ +/*! + * jQuery JavaScript Library v1.5.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Mar 31 15:28:23 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.5.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + // A third-party is pushing the ready event forwards + if ( wait === true ) { + jQuery.readyWait--; + } + + // Make sure that the DOM is not already loaded + if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery._Deferred(); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", DOMContentLoaded); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test(data.replace(rvalidescape, "@") + .replace(rvalidtokens, "]") + .replace(rvalidbraces, "")) ) { + + // Try to use the native JSON parser first + return window.JSON && window.JSON.parse ? + window.JSON.parse( data ) : + (new Function("return " + data))(); + + } else { + jQuery.error( "Invalid JSON: " + data ); + } + }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + + noop: function() {}, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && rnotwhite.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement, + script = document.createElement( "script" ); + + if ( jQuery.support.scriptEval() ) { + script.appendChild( document.createTextNode( data ) ); + } else { + script.text = data; + } + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction(object); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type(array); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var ret = [], value; + + // Go through the array, translating each of the items to their + // new value (or values). + for ( var i = 0, length = elems.length; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + proxy: function( fn, proxy, thisObject ) { + if ( arguments.length === 2 ) { + if ( typeof proxy === "string" ) { + thisObject = fn; + fn = thisObject[ proxy ]; + proxy = undefined; + + } else if ( proxy && !jQuery.isFunction( proxy ) ) { + thisObject = proxy; + proxy = undefined; + } + } + + if ( !proxy && fn ) { + proxy = function() { + return fn.apply( thisObject || this, arguments ); + }; + } + + // Set the guid of unique handler to the same of original handler, so it can be removed + if ( fn ) { + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + } + + // So proxy can be declared as an argument + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can be optionally by executed if its a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySubclass( selector, context ) { + return new jQuerySubclass.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySubclass, this ); + jQuerySubclass.superclass = this; + jQuerySubclass.fn = jQuerySubclass.prototype = this(); + jQuerySubclass.fn.constructor = jQuerySubclass; + jQuerySubclass.subclass = this.subclass; + jQuerySubclass.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySubclass) ) { + context = jQuerySubclass(context); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass ); + }; + jQuerySubclass.fn.init.prototype = jQuerySubclass.fn; + var rootjQuerySubclass = jQuerySubclass(document); + return jQuerySubclass; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +if ( indexOf ) { + jQuery.inArray = function( elem, array ) { + return indexOf.call( array, elem ); + }; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +// Expose jQuery to the global object +return jQuery; + +})(); + + +var // Promise methods + promiseMethods = "then done fail isResolved isRejected promise".split( " " ), + // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + // make sure args are available (#8421) + args = args || []; + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + } ); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = arguments, + i = 0, + length = args.length, + count = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + // Strange bug in FF4: + // Values changed onto the arguments object sometimes end up as undefined values + // outside the $.when method. Cloning the object into a fresh array solves the issue + deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + } + }; + } + if ( length > 1 ) { + for( ; i < length; i++ ) { + if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return deferred.promise(); + } +}); + + + + +(function() { + + jQuery.support = {}; + + var div = document.createElement("div"); + + div.style.display = "none"; + div.innerHTML = "
        a"; + + var all = div.getElementsByTagName("*"), + a = div.getElementsByTagName("a")[0], + select = document.createElement("select"), + opt = select.appendChild( document.createElement("option") ), + input = div.getElementsByTagName("input")[0]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return; + } + + jQuery.support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText insted) + style: /red/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute("href") === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: input.value === "on", + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Will be defined later + deleteExpando: true, + optDisabled: false, + checkClone: false, + noCloneEvent: true, + noCloneChecked: true, + boxModel: null, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableHiddenOffsets: true, + reliableMarginRight: true + }; + + input.checked = true; + jQuery.support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as diabled) + select.disabled = true; + jQuery.support.optDisabled = !opt.disabled; + + var _scriptEval = null; + jQuery.support.scriptEval = function() { + if ( _scriptEval === null ) { + var root = document.documentElement, + script = document.createElement("script"), + id = "script" + jQuery.now(); + + // Make sure that the execution of code works by injecting a script + // tag with appendChild/createTextNode + // (IE doesn't support this, fails, and uses .text instead) + try { + script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); + } catch(e) {} + + root.insertBefore( script, root.firstChild ); + + if ( window[ id ] ) { + _scriptEval = true; + delete window[ id ]; + } else { + _scriptEval = false; + } + + root.removeChild( script ); + } + + return _scriptEval; + }; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + + } catch(e) { + jQuery.support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent("onclick", function click() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + jQuery.support.noCloneEvent = false; + div.detachEvent("onclick", click); + }); + div.cloneNode(true).fireEvent("onclick"); + } + + div = document.createElement("div"); + div.innerHTML = ""; + + var fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; + + // Figure out if the W3C box model works as expected + // document.body must exist before we can do this + jQuery(function() { + var div = document.createElement("div"), + body = document.getElementsByTagName("body")[0]; + + // Frameset documents with no body should not run this code + if ( !body ) { + return; + } + + div.style.width = div.style.paddingLeft = "1px"; + body.appendChild( div ); + jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2; + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
        "; + jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2; + } + + div.innerHTML = "
        t
        "; + var tds = div.getElementsByTagName("td"); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0; + + tds[0].style.display = ""; + tds[1].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0; + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + div.style.width = "1px"; + div.style.marginRight = "0"; + jQuery.support.reliableMarginRight = ( parseInt(document.defaultView.getComputedStyle(div, null).marginRight, 10) || 0 ) === 0; + } + + body.removeChild( div ).style.display = "none"; + div = tds = null; + }); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + var eventSupported = function( eventName ) { + var el = document.createElement("div"); + eventName = "on" + eventName; + + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( !el.attachEvent ) { + return true; + } + + var isSupported = (eventName in el); + if ( !isSupported ) { + el.setAttribute(eventName, "return;"); + isSupported = typeof el[eventName] === "function"; + } + return isSupported; + }; + + jQuery.support.submitBubbles = eventSupported("submit"); + jQuery.support.changeBubbles = eventSupported("change"); + + // release memory in IE + div = all = a = null; +})(); + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ name ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + return getByName ? thisCache[ name ] : thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + if ( jQuery.support.deleteExpando || cache != window ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = name.substr( 5 ); + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + data = elem.getAttribute( "data-" + key ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +jQuery.extend({ + queue: function( elem, type, data ) { + if ( !elem ) { + return; + } + + type = (type || "fx") + "queue"; + var q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( !data ) { + return q || []; + } + + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + + } else { + q.push( data ); + } + + return q; + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(); + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function( i ) { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspaces = /\s+/, + rreturn = /\r/g, + rspecialurl = /^(?:href|src|style)$/, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rradiocheck = /^(?:radio|checkbox)$/i; + +jQuery.props = { + "for": "htmlFor", + "class": "className", + readonly: "readOnly", + maxlength: "maxLength", + cellspacing: "cellSpacing", + rowspan: "rowSpan", + colspan: "colSpan", + tabindex: "tabIndex", + usemap: "useMap", + frameborder: "frameBorder" +}; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name, fn ) { + return this.each(function(){ + jQuery.attr( this, name, "" ); + if ( this.nodeType === 1 ) { + this.removeAttribute( name ); + } + }); + }, + + addClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.addClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspaces ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; + + } else { + var className = " " + elem.className + " ", + setClass = elem.className; + + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.removeClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split( rspaces ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this); + self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspaces ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + if ( !arguments.length ) { + var elem = this[0]; + + if ( elem ) { + if ( jQuery.nodeName( elem, "option" ) ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + } + + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(rreturn, ""); + + } + + return undefined; + } + + var isFunction = jQuery.isFunction(value); + + return this.each(function(i) { + var self = jQuery(this), val = value; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call(this, i, self.val()); + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray(val) ) { + val = jQuery.map(val, function (value) { + return value == null ? "" : value + ""; + }); + } + + if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { + this.checked = jQuery.inArray( self.val(), val ) >= 0; + + } else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(val); + + jQuery( "option", this ).each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + this.selectedIndex = -1; + } + + } else { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery(elem)[name](value); + } + + var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), + // Whether we are setting (or getting) + set = value !== undefined; + + // Try to normalize/fix the name + name = notxml && jQuery.props[ name ] || name; + + // Only do all the following if this is a node (faster for style) + if ( elem.nodeType === 1 ) { + // These attributes require special treatment + var special = rspecialurl.test( name ); + + // Safari mis-reports the default selected property of an option + // Accessing the parent's selectedIndex property fixes it + if ( name === "selected" && !jQuery.support.optSelected ) { + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + + // If applicable, access the attribute via the DOM 0 way + // 'in' checks fail in Blackberry 4.7 #6931 + if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) { + if ( set ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } + + if ( value === null ) { + if ( elem.nodeType === 1 ) { + elem.removeAttribute( name ); + } + + } else { + elem[ name ] = value; + } + } + + // browsers index elements by id/name on forms, give priority to attributes. + if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { + return elem.getAttributeNode( name ).nodeValue; + } + + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + if ( name === "tabIndex" ) { + var attributeNode = elem.getAttributeNode( "tabIndex" ); + + return attributeNode && attributeNode.specified ? + attributeNode.value : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + + return elem[ name ]; + } + + if ( !jQuery.support.style && notxml && name === "style" ) { + if ( set ) { + elem.style.cssText = "" + value; + } + + return elem.style.cssText; + } + + if ( set ) { + // convert the value to a string (all browsers do this but IE) see #1070 + elem.setAttribute( name, "" + value ); + } + + // Ensure that missing attributes return undefined + // Blackberry 4.7 returns "" from getAttribute #6938 + if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) { + return undefined; + } + + var attr = !jQuery.support.hrefNormalized && notxml && special ? + // Some attributes require a special call on IE + elem.getAttribute( name, 2 ) : + elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return attr === null ? undefined : attr; + } + // Handle everything which isn't a DOM element node + if ( set ) { + elem[ name ] = value; + } + return elem[ name ]; + } +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspace = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // TODO :: Use a try/catch until it's safe to pull this out (likely 1.6) + // Minor release fix for bug #8018 + try { + // For whatever reason, IE has trouble passing the window object + // around, causing it to be cloned in the process + if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) { + elem = window; + } + } + catch ( e ) {} + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Handle the second event of a trigger and when + // an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for global triggering + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // bubbling is internal + trigger: function( event, data, elem /*, bubbling */ ) { + // Event object or event type + var type = event.type || event, + bubbling = arguments[3]; + + if ( !bubbling ) { + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + jQuery.extend( jQuery.Event(type), event ) : + // Just the event type (string) + jQuery.Event(type); + + if ( type.indexOf("!") >= 0 ) { + event.type = type = type.slice(0, -1); + event.exclusive = true; + } + + // Handle a global trigger + if ( !elem ) { + // Don't bubble custom events when global (to avoid too much overhead) + event.stopPropagation(); + + // Only trigger if we've ever bound an event for it + if ( jQuery.event.global[ type ] ) { + // XXX This code smells terrible. event.js should not be directly + // inspecting the data cache + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + } + } + + // Handle triggering a single element + + // don't do events on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { + return undefined; + } + + // Clean up in case it is reused + event.result = undefined; + event.target = elem; + + // Clone the incoming data, if any + data = jQuery.makeArray( data ); + data.unshift( event ); + } + + event.currentTarget = elem; + + // Trigger the event, it is assumed that "handle" is a function + var handle = jQuery._data( elem, "handle" ); + + if ( handle ) { + handle.apply( elem, data ); + } + + var parent = elem.parentNode || elem.ownerDocument; + + // Trigger an inline bound script + try { + if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) { + if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) { + event.result = false; + event.preventDefault(); + } + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (inlineError) {} + + if ( !event.isPropagationStopped() && parent ) { + jQuery.event.trigger( event, data, parent, true ); + + } else if ( !event.isDefaultPrevented() ) { + var old, + target = event.target, + targetType = type.replace( rnamespaces, "" ), + isClick = jQuery.nodeName( target, "a" ) && targetType === "click", + special = jQuery.event.special[ targetType ] || {}; + + if ( (!special._default || special._default.call( elem, event ) === false) && + !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { + + try { + if ( target[ targetType ] ) { + // Make sure that we don't accidentally re-trigger the onFOO events + old = target[ "on" + targetType ]; + + if ( old ) { + target[ "on" + targetType ] = null; + } + + jQuery.event.triggered = event.type; + target[ targetType ](); + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (triggerError) {} + + if ( old ) { + target[ "on" + targetType ] = old; + } + + jQuery.event.triggered = undefined; + } + } + }, + + handle: function( event ) { + var all, handlers, namespaces, namespace_re, events, + namespace_sort = [], + args = jQuery.makeArray( arguments ); + + event = args[0] = jQuery.event.fix( event || window.event ); + event.currentTarget = this; + + // Namespaced event handlers + all = event.type.indexOf(".") < 0 && !event.exclusive; + + if ( !all ) { + namespaces = event.type.split("."); + event.type = namespaces.shift(); + namespace_sort = namespaces.slice(0).sort(); + namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.namespace = event.namespace || namespace_sort.join("."); + + events = jQuery._data(this, "events"); + + handlers = (events || {})[ event.type ]; + + if ( events && handlers ) { + // Clone the handlers to prevent manipulation + handlers = handlers.slice(0); + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Filter the functions by class + if ( all || namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + } + + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var doc = document.documentElement, + body = document.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + // Check if mouse(over|out) are still within the same parent element + var parent = event.relatedTarget; + + // Firefox sometimes assigns relatedTarget a XUL element + // which we cannot access the parentNode property of + try { + + // Chrome does something similar, the parentNode property + // can be accessed but is null. + if ( parent && parent !== document && !parent.parentNode ) { + return; + } + // Traverse up the tree + while ( parent && parent !== this ) { + parent = parent.parentNode; + } + + if ( parent !== this ) { + // set the correct event type + event.type = event.data; + + // handle event if we actually just moused on to a non sub-element + jQuery.event.handle.apply( this, arguments ); + } + + // assuming we've left the element since we most likely mousedover a xul element + } catch(e) { } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( elem.nodeName.toLowerCase() === "select" ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = elem.type; + + if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = elem.type; + + if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( document.addEventListener ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + + function handler( donor ) { + // Donor event is always a native one; fix it and switch its type. + // Let focusin/out handler cancel the donor focus/blur event. + var e = jQuery.event.fix( donor ); + e.type = fix; + e.originalEvent = {}; + jQuery.event.trigger( e, null, e.target ); + if ( e.isDefaultPrevented() ) { + donor.preventDefault(); + } + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( jQuery.isFunction( data ) || data === false ) { + fn = data; + data = undefined; + } + + var handler = name === "one" ? jQuery.proxy( fn, function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }) : fn; + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + var event = jQuery.Event( type ); + event.preventDefault(); + event.stopPropagation(); + jQuery.event.trigger( event, data, this[0] ); + return event.result; + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + i = 1; + + // link all the functions, so any of them can unbind this click handler + while ( i < args.length ) { + jQuery.proxy( fn, args[ i++ ] ); + } + + return this.click( jQuery.proxy( fn, function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + })); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( jQuery.isFunction( data ) ) { + fn = data; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( type === "focus" || type === "blur" ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return "radio" === elem.type; + }, + + checkbox: function( elem ) { + return "checkbox" === elem.type; + }, + + file: function( elem ) { + return "file" === elem.type; + }, + password: function( elem ) { + return "password" === elem.type; + }, + + submit: function( elem ) { + return "submit" === elem.type; + }, + + image: function( elem ) { + return "image" === elem.type; + }, + + reset: function( elem ) { + return "reset" === elem.type; + }, + + button: function( elem ) { + return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // If the nodes are siblings (or identical) we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

        "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
        "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var ret = this.pushStack( "", "find", selector ), + length = 0; + + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && jQuery.filter( selector, this ).length > 0; + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[selector] ) { + matches[selector] = jQuery.expr.match.POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[selector]; + + if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + var pos = POS.test( selectors ) ? + jQuery( selectors, context || this.context ) : null; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique(ret) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
        ", "
        " ], + thead: [ 1, "", "
        " ], + tr: [ 2, "", "
        " ], + td: [ 3, "", "
        " ], + col: [ 2, "", "
        " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + + +
        + + +
        + + + + +
        +

        Getting started

        +

        Here’s how to get rolling:

        + +
          +
        1. +

          Use script/generate to create your models and controllers

          +

          To see all available options, run it without parameters.

          +
        2. + +
        3. +

          Set up a default route and remove or rename this file

          +

          Routes are set up in config/routes.rb.

          +
        4. + +
        5. +

          Create your database

          +

          Run rake db:migrate to create your database. If you're not using SQLite (the default), edit config/database.yml with your username and password.

          +
        6. +
        +
        +
        + + +
        + + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/application.js b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/application.js new file mode 100644 index 00000000000..fe4577696b2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/application.js @@ -0,0 +1,2 @@ +// Place your application-specific JavaScript functions and classes here +// This file is automatically included by javascript_include_tag :defaults diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/controls.js b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/controls.js new file mode 100644 index 00000000000..ca29aefdd1f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/controls.js @@ -0,0 +1,963 @@ +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2008 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 = Class.create({ + 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; + this.oldElementValue = this.element.value; + + 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); + // Force carriage returns as token delimiters anyway + if (!this.options.tokens.include('\n')) + this.options.tokens.push('\n'); + + 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, 'keydown', this.onKeyPress.bindAsEventListener(this)); + }, + + 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(); + Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + 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 = $(selectedElement).select('.' + this.options.select) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var bounds = this.getTokenBounds(); + if (bounds[0] != -1) { + var newValue = this.element.value.substr(0, bounds[0]); + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value + this.element.value.substr(bounds[1]); + } else { + this.element.value = value; + } + this.oldElementValue = this.element.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; + this.tokenBounds = null; + if(this.getToken().length>=this.options.minChars) { + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + this.oldElementValue = this.element.value; + }, + + getToken: function() { + var bounds = this.getTokenBounds(); + return this.element.value.substring(bounds[0], bounds[1]).strip(); + }, + + getTokenBounds: function() { + if (null != this.tokenBounds) return this.tokenBounds; + var value = this.element.value; + if (value.strip().empty()) return [-1, 0]; + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); + var offset = (diff == this.oldElementValue.length ? 1 : 0); + var prevTokenPos = -1, nextTokenPos = value.length; + var tp; + for (var index = 0, l = this.options.tokens.length; index < l; ++index) { + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); + if (tp > prevTokenPos) prevTokenPos = tp; + tp = value.indexOf(this.options.tokens[index], diff + offset); + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; + } + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); + } +}); + +Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { + var boundary = Math.min(newS.length, oldS.length); + for (var index = 0; index < boundary; ++index) + if (newS[index] != oldS[index]) + return index; + return boundary; +}; + +Ajax.Autocompleter = Class.create(Autocompleter.Base, { + 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.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 and collection editor +// Full rewrite by Christophe Porteneuve (April 2007). + +// 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({ + initialize: function(element, url, options) { + this.url = url; + this.element = element = $(element); + this.prepareOptions(); + this._controls = { }; + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! + Object.extend(this.options, options || { }); + if (!this.options.formId && this.element.id) { + this.options.formId = this.element.id + '-inplaceeditor'; + if ($(this.options.formId)) + this.options.formId = ''; + } + if (this.options.externalControl) + this.options.externalControl = $(this.options.externalControl); + if (!this.options.externalControl) + this.options.externalControlOnly = false; + this._originalBackground = this.element.getStyle('background-color') || 'transparent'; + this.element.title = this.options.clickToEditText; + this._boundCancelHandler = this.handleFormCancellation.bind(this); + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); + this._boundFailureHandler = this.handleAJAXFailure.bind(this); + this._boundSubmitHandler = this.handleFormSubmission.bind(this); + this._boundWrapperHandler = this.wrapUp.bind(this); + this.registerListeners(); + }, + checkForEscapeOrReturn: function(e) { + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; + if (Event.KEY_ESC == e.keyCode) + this.handleFormCancellation(e); + else if (Event.KEY_RETURN == e.keyCode) + this.handleFormSubmission(e); + }, + createControl: function(mode, handler, extraClasses) { + var control = this.options[mode + 'Control']; + var text = this.options[mode + 'Text']; + if ('button' == control) { + var btn = document.createElement('input'); + btn.type = 'submit'; + btn.value = text; + btn.className = 'editor_' + mode + '_button'; + if ('cancel' == mode) + btn.onclick = this._boundCancelHandler; + this._form.appendChild(btn); + this._controls[mode] = btn; + } else if ('link' == control) { + var link = document.createElement('a'); + link.href = '#'; + link.appendChild(document.createTextNode(text)); + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; + link.className = 'editor_' + mode + '_link'; + if (extraClasses) + link.className += ' ' + extraClasses; + this._form.appendChild(link); + this._controls[mode] = link; + } + }, + createEditField: function() { + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); + var fld; + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { + fld = document.createElement('input'); + fld.type = 'text'; + var size = this.options.size || this.options.cols || 0; + if (0 < size) fld.size = size; + } else { + fld = document.createElement('textarea'); + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); + fld.cols = this.options.cols || 40; + } + fld.name = this.options.paramName; + fld.value = text; // No HTML breaks conversion anymore + fld.className = 'editor_field'; + if (this.options.submitOnBlur) + fld.onblur = this._boundSubmitHandler; + this._controls.editor = fld; + if (this.options.loadTextURL) + this.loadExternalText(); + this._form.appendChild(this._controls.editor); + }, + createForm: function() { + var ipe = this; + function addText(mode, condition) { + var text = ipe.options['text' + mode + 'Controls']; + if (!text || condition === false) return; + ipe._form.appendChild(document.createTextNode(text)); + }; + this._form = $(document.createElement('form')); + this._form.id = this.options.formId; + this._form.addClassName(this.options.formClassName); + this._form.onsubmit = this._boundSubmitHandler; + this.createEditField(); + if ('textarea' == this._controls.editor.tagName.toLowerCase()) + this._form.appendChild(document.createElement('br')); + if (this.options.onFormCustomization) + this.options.onFormCustomization(this, this._form); + addText('Before', this.options.okControl || this.options.cancelControl); + this.createControl('ok', this._boundSubmitHandler); + addText('Between', this.options.okControl && this.options.cancelControl); + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); + addText('After', this.options.okControl || this.options.cancelControl); + }, + destroy: function() { + if (this._oldInnerHTML) + this.element.innerHTML = this._oldInnerHTML; + this.leaveEditMode(); + this.unregisterListeners(); + }, + enterEditMode: function(e) { + if (this._saving || this._editing) return; + this._editing = true; + this.triggerCallback('onEnterEditMode'); + if (this.options.externalControl) + this.options.externalControl.hide(); + this.element.hide(); + this.createForm(); + this.element.parentNode.insertBefore(this._form, this.element); + if (!this.options.loadTextURL) + this.postProcessEditField(); + if (e) Event.stop(e); + }, + enterHover: function(e) { + if (this.options.hoverClassName) + this.element.addClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onEnterHover'); + }, + getText: function() { + return this.element.innerHTML.unescapeHTML(); + }, + handleAJAXFailure: function(transport) { + this.triggerCallback('onFailure', transport); + if (this._oldInnerHTML) { + this.element.innerHTML = this._oldInnerHTML; + this._oldInnerHTML = null; + } + }, + handleFormCancellation: function(e) { + this.wrapUp(); + if (e) Event.stop(e); + }, + handleFormSubmission: function(e) { + var form = this._form; + var value = $F(this._controls.editor); + this.prepareSubmission(); + var params = this.options.callback(form, value) || ''; + if (Object.isString(params)) + params = params.toQueryParams(); + params.editorId = this.element.id; + if (this.options.htmlResponse) { + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Updater({ success: this.element }, this.url, options); + } else { + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.url, options); + } + if (e) Event.stop(e); + }, + leaveEditMode: function() { + this.element.removeClassName(this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + if (this.options.externalControl) + this.options.externalControl.show(); + this._saving = false; + this._editing = false; + this._oldInnerHTML = null; + this.triggerCallback('onLeaveEditMode'); + }, + leaveHover: function(e) { + if (this.options.hoverClassName) + this.element.removeClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onLeaveHover'); + }, + loadExternalText: function() { + this._form.addClassName(this.options.loadingClassName); + this._controls.editor.disabled = true; + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._form.removeClassName(this.options.loadingClassName); + var text = transport.responseText; + if (this.options.stripLoadedTextTags) + text = text.stripTags(); + this._controls.editor.value = text; + this._controls.editor.disabled = false; + this.postProcessEditField(); + }.bind(this), + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + postProcessEditField: function() { + var fpc = this.options.fieldPostCreation; + if (fpc) + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); + }, + prepareOptions: function() { + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); + [this._extraDefaultOptions].flatten().compact().each(function(defs) { + Object.extend(this.options, defs); + }.bind(this)); + }, + prepareSubmission: function() { + this._saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + registerListeners: function() { + this._listeners = { }; + var listener; + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { + listener = this[pair.value].bind(this); + this._listeners[pair.key] = listener; + if (!this.options.externalControlOnly) + this.element.observe(pair.key, listener); + if (this.options.externalControl) + this.options.externalControl.observe(pair.key, listener); + }.bind(this)); + }, + removeForm: function() { + if (!this._form) return; + this._form.remove(); + this._form = null; + this._controls = { }; + }, + showSaving: function() { + this._oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + this.element.addClassName(this.options.savingClassName); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + }, + triggerCallback: function(cbName, arg) { + if ('function' == typeof this.options[cbName]) { + this.options[cbName](this, arg); + } + }, + unregisterListeners: function() { + $H(this._listeners).each(function(pair) { + if (!this.options.externalControlOnly) + this.element.stopObserving(pair.key, pair.value); + if (this.options.externalControl) + this.options.externalControl.stopObserving(pair.key, pair.value); + }.bind(this)); + }, + wrapUp: function(transport) { + this.leaveEditMode(); + // Can't use triggerCallback due to backward compatibility: requires + // binding + direct element + this._boundComplete(transport, this.element); + } +}); + +Object.extend(Ajax.InPlaceEditor.prototype, { + dispose: Ajax.InPlaceEditor.prototype.destroy +}); + +Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { + initialize: function($super, element, url, options) { + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; + $super(element, url, options); + }, + + createEditField: function() { + var list = document.createElement('select'); + list.name = this.options.paramName; + list.size = 1; + this._controls.editor = list; + this._collection = this.options.collection || []; + if (this.options.loadCollectionURL) + this.loadCollection(); + else + this.checkForExternalText(); + this._form.appendChild(this._controls.editor); + }, + + loadCollection: function() { + this._form.addClassName(this.options.loadingClassName); + this.showLoadingText(this.options.loadingCollectionText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + var js = transport.responseText.strip(); + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check + throw('Server returned an invalid collection representation.'); + this._collection = eval(js); + this.checkForExternalText(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadCollectionURL, options); + }, + + showLoadingText: function(text) { + this._controls.editor.disabled = true; + var tempOption = this._controls.editor.firstChild; + if (!tempOption) { + tempOption = document.createElement('option'); + tempOption.value = ''; + this._controls.editor.appendChild(tempOption); + tempOption.selected = true; + } + tempOption.update((text || '').stripScripts().stripTags()); + }, + + checkForExternalText: function() { + this._text = this.getText(); + if (this.options.loadTextURL) + this.loadExternalText(); + else + this.buildOptionList(); + }, + + loadExternalText: function() { + this.showLoadingText(this.options.loadingText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._text = transport.responseText.strip(); + this.buildOptionList(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + + buildOptionList: function() { + this._form.removeClassName(this.options.loadingClassName); + this._collection = this._collection.map(function(entry) { + return 2 === entry.length ? entry : [entry, entry].flatten(); + }); + var marker = ('value' in this.options) ? this.options.value : this._text; + var textFound = this._collection.any(function(entry) { + return entry[0] == marker; + }.bind(this)); + this._controls.editor.update(''); + var option; + this._collection.each(function(entry, index) { + option = document.createElement('option'); + option.value = entry[0]; + option.selected = textFound ? entry[0] == marker : 0 == index; + option.appendChild(document.createTextNode(entry[1])); + this._controls.editor.appendChild(option); + }.bind(this)); + this._controls.editor.disabled = false; + Field.scrollFreeActivate(this._controls.editor); + } +}); + +//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** +//**** This only exists for a while, in order to let **** +//**** users adapt to the new API. Read up on the new **** +//**** API and convert your code to it ASAP! **** + +Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { + if (!options) return; + function fallback(name, expr) { + if (name in options || expr === undefined) return; + options[name] = expr; + }; + fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : + options.cancelLink == options.cancelButton == false ? false : undefined))); + fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : + options.okLink == options.okButton == false ? false : undefined))); + fallback('highlightColor', options.highlightcolor); + fallback('highlightEndColor', options.highlightendcolor); +}; + +Object.extend(Ajax.InPlaceEditor, { + DefaultOptions: { + ajaxOptions: { }, + autoRows: 3, // Use when multi-line w/ rows == 1 + cancelControl: 'link', // 'link'|'button'|false + cancelText: 'cancel', + clickToEditText: 'Click to edit', + externalControl: null, // id|elt + externalControlOnly: false, + fieldPostCreation: 'activate', // 'activate'|'focus'|false + formClassName: 'inplaceeditor-form', + formId: null, // id|elt + highlightColor: '#ffff99', + highlightEndColor: '#ffffff', + hoverClassName: '', + htmlResponse: true, + loadingClassName: 'inplaceeditor-loading', + loadingText: 'Loading...', + okControl: 'button', // 'link'|'button'|false + okText: 'ok', + paramName: 'value', + rows: 1, // If 1 and multi-line, uses autoRows + savingClassName: 'inplaceeditor-saving', + savingText: 'Saving...', + size: 0, + stripLoadedTextTags: false, + submitOnBlur: false, + textAfterControls: '', + textBeforeControls: '', + textBetweenControls: '' + }, + DefaultCallbacks: { + callback: function(form) { + return Form.serialize(form); + }, + onComplete: function(transport, element) { + // For backward compatibility, this one is bound to the IPE, and passes + // the element directly. It was too often customized, so we don't break it. + new Effect.Highlight(element, { + startcolor: this.options.highlightColor, keepBackgroundImage: true }); + }, + onEnterEditMode: null, + onEnterHover: function(ipe) { + ipe.element.style.backgroundColor = ipe.options.highlightColor; + if (ipe._effect) + ipe._effect.cancel(); + }, + onFailure: function(transport, ipe) { + alert('Error communication with the server: ' + transport.responseText.stripTags()); + }, + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. + onLeaveEditMode: null, + onLeaveHover: function(ipe) { + ipe._effect = new Effect.Highlight(ipe.element, { + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, + restorecolor: ipe._originalBackground, keepBackgroundImage: true + }); + } + }, + Listeners: { + click: 'enterEditMode', + keydown: 'checkForEscapeOrReturn', + mouseover: 'enterHover', + mouseout: 'leaveHover' + } +}); + +Ajax.InPlaceCollectionEditor.DefaultOptions = { + loadingCollectionText: 'Loading options...' +}; + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create({ + 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)); + } +}); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/dragdrop.js b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/dragdrop.js new file mode 100644 index 00000000000..07229f986f5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/dragdrop.js @@ -0,0 +1,973 @@ +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 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(Object.isUndefined(Effect)) + 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(Object.isArray(containment)) { + 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 drop, affected = []; + + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) + drop = Droppables.findDeepestChild(affected); + + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); + if (drop) { + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + if (drop != this.last_active) 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({ + 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 = Object.isNumber(element._opacity) ? 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] || Object.isUndefined(arguments[1].endeffect)) + 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 && Object.isString(options.handle)) + 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.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(!Object.isUndefined(Draggable._dragging[this.element]) && + 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.delta) + this.delta = this.currentDelta(); + + 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); + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) + 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) { + if (!this._originallyAbsolute) + Position.relativize(this.element); + delete this._originallyAbsolute; + 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 && Object.isFunction(revert)) 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(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*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 }; + } +}); + +Draggable._dragging = { }; + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create({ + 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){ + element = $(element); + var s = Sortable.sortables[element.id]; + + 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).select('.' + 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')]; +}; \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/effects.js b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/effects.js new file mode 100644 index 00000000000..5a639d2dea9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/effects.js @@ -0,0 +1,1128 @@ +// Copyright (c) 2005-2008 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) { } +}; + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + .5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; + }, + pulse: function(pos, pulses) { + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, + tagifyText: function(element) { + 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( + new Element('span', {style: tagifyStyle}).update( + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if (((typeof element == 'object') || + Object.isFunction(element)) && + (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); + } +}; + +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(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 = Object.isString(effect.options.queue) ? + 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 = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + '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 (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#'; + } +}); + +Effect.Parallel = Class.create(Effect.Base, { + 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.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { + 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(Effect.Base, { + 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() { + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if (this.options.mode == 'absolute') { + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + '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(Effect.Base, { + 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 = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + '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(Effect.Base, { + 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+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()); } + ); +}; + +/* ------------- 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 options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance, y: 0, duration: split, 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'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + 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().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, 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] || { }, + oldOpacity = element.getInlineOpacity(), + transition = options.transition || Effect.Transitions.linear, + reverser = function(pos){ + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); + }; + + 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(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.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 + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: 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 ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, 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.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '
        '; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); + + return styleRules; +}; + +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { + results[property] = css[property]; + return results; + }); + if (!styles.opacity) styles.opacity = element.getOpacity(); + return styles; + }; +} + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element); + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + }; + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/prototype.js b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/prototype.js new file mode 100644 index 00000000000..9fe6e1243bc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/javascripts/prototype.js @@ -0,0 +1,4874 @@ +/* Prototype JavaScript framework, version 1.6.1 + * (c) 2005-2009 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.6.1', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile.*Safari/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + SelectorsAPI: !!document.querySelector, + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'); + var form = document.createElement('form'); + var isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +var Abstract = { }; + + +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; + } +}; + +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0; i < properties.length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames().first() == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = method.valueOf.bind(method); + value.toString = method.toString.bind(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString; + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(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 (isElement(object)) return; + + var results = []; + for (var property in object) { + var value = toJSON(object[property]); + if (!isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + var results = []; + for (var property in object) + results.push(property); + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) == "[object Array]"; + } + + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return typeof object === "function"; + } + + function isString(object) { + return _toString.call(object) == "[object String]"; + } + + function isNumber(object) { + return _toString.call(object) == "[object Number]"; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = slice.call(arguments, 1); + return function() { + var a = merge(args, arguments); + return __method.apply(context, a); + } + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000 + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + return { + argumentNames: argumentNames, + bind: bind, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + } +})()); + + +Date.prototype.toJSON = function() { + return '"' + this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z"'; +}; + + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; +var PeriodicalExecuter = Class.create({ + 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); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +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, (function() { + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || pattern.source)) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + 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; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + 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]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script) }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(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 (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + 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; + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function toJSON() { + return this.inspect(true); + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + var str = this; + if (str.blank()) return false; + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern) { + return this.indexOf(pattern) === 0; + } + + function endsWith(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim ? String.prototype.trim : strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + toJSON: toJSON, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: evalJSON, + include: include, + startsWith: startsWith, + endsWith: endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = (function() { + function each(iterator, context) { + var index = 0; + try { + this._each(function(value) { + iterator.call(context, value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(number, iterator, context) { + var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + } + + function all(iterator, context) { + iterator = iterator || Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator.call(context, value, index); + if (!result) throw $break; + }); + return result; + } + + function any(iterator, context) { + iterator = iterator || Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator.call(context, value, index)) + throw $break; + }); + return result; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index)); + }); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index)) { + result = value; + throw $break; + } + }); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index)) + results.push(value); + }); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index)); + }); + return results; + } + + function include(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index); + }); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index); + if (result == null || value >= result) + result = value; + }); + return result; + } + + function min(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index); + if (result == null || value < result) + result = value; + }); + return result; + } + + function partition(iterator, context) { + iterator = iterator || Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator.call(context, value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index)) + results.push(value); + }); + return results; + } + + function sortBy(iterator, context) { + return this.map(function(value, index) { + return { + value: value, + criteria: iterator.call(context, value, index) + }; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + } + + function toArray() { + return this.map(); + } + + function zip() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function toJSON() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } + + function indexOf(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; + } + + function lastIndexOf(item, i) { + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; + var n = this.slice(0, i).reverse().indexOf(item); + return (n < 0) ? n : i - n - 1; + } + + function concat() { + var array = slice.call(this, 0), item; + for (var i = 0, length = arguments.length; i < length; i++) { + item = arguments[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) + array.push(item[j]); + } else { + array.push(item); + } + } + return array; + } + + Object.extend(arrayProto, Enumerable); + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect, + toJSON: toJSON + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2) + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + function _each(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + function toQueryString() { + return this.inject([], function(results, pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) + return results.concat(values.map(toQueryPair.curry(key))); + } else results.push(toQueryPair(key, values)); + return results; + }).join('&'); + } + + function inspect() { + return '#'; + } + + function toJSON() { + return Object.toJSON(this.toObject()); + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toJSON, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function toJSON() { + return isFinite(this) ? this.toString() : 'null'; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + toJSON: toJSON, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +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 (Object.isFunction(responder[callback])) { + 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 = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isString(this.options.parameters)) + this.options.parameters = this.options.parameters.toQueryParams(); + else if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + 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)) { + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Object.toQueryString(params)) { + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + 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'; + } + + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + 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() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300); + }, + + getStatus: function() { + try { + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || 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.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + + + + + + + + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + json = decodeURIComponent(escape(json)); + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(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(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + 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 (Object.isString(element)) + 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(Element.extend(query.snapshotItem(i))); + return results; + }; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Node) var Node = { }; + +if (!Node.ELEMENT_NODE) { + Object.extend(Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); +} + + +(function(global) { + + var SETATTRIBUTE_IGNORES_NAME = (function(){ + var elForm = document.createElement("form"); + var elInput = document.createElement("input"); + var root = document.documentElement; + elInput.setAttribute("name", "test"); + elForm.appendChild(elInput); + root.appendChild(elForm); + var isBuggy = elForm.elements + ? (typeof elForm.elements.test == "undefined") + : null; + root.removeChild(elForm); + elForm = elInput = null; + return isBuggy; + })(); + + var element = global.Element; + global.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (SETATTRIBUTE_IGNORES_NAME && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + }; + Object.extend(global.Element, element || { }); + if (element) global.Element.prototype = element.prototype; +})(this); + +Element.cache = { }; +Element.idCounter = 1; + +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 = $(element); + element.style.display = 'none'; + return element; + }, + + show: function(element) { + element = $(element); + element.style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: (function(){ + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(element, content) { + element = $(element); + + if (content && content.toElement) + content = content.toElement(); + + if (Object.isElement(content)) + return element.update().insert(content); + + content = Object.toHTML(content); + + var tagName = element.tagName.toUpperCase(); + + if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { + if (tagName in Element._insertionTranslations.tags) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { + element.appendChild(node) + }); + } + else { + element.innerHTML = content.stripScripts(); + } + } + else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + return update; + })(), + + replace: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + element.parentNode.replaceChild(content, element); + return element; + }, + + insert: function(element, insertions) { + element = $(element); + + if (Object.isString(insertions) || Object.isNumber(insertions) || + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) + insertions = {bottom:insertions}; + + var content, insert, tagName, childNodes; + + for (var position in insertions) { + content = insertions[position]; + position = position.toLowerCase(); + insert = Element._insertionTranslations[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + insert(element, content); + continue; + } + + content = Object.toHTML(content); + + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); + + content.evalScripts.bind(content).defer(); + } + + return element; + }, + + wrap: function(element, wrapper, attributes) { + element = $(element); + if (Object.isElement(wrapper)) + $(wrapper).writeAttribute(attributes || { }); + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); + else wrapper = new Element('div', wrapper); + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + wrapper.appendChild(element); + return wrapper; + }, + + 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(element, 'parentNode'); + }, + + descendants: function(element) { + return Element.select(element, "*"); + }, + + 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(element, 'previousSibling'); + }, + + nextSiblings: function(element) { + return Element.recursivelyCollect(element, 'nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return Element.previousSiblings(element).reverse() + .concat(Element.nextSiblings(element)); + }, + + match: function(element, selector) { + if (Object.isString(selector)) + 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(element); + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return Element.firstDescendant(element); + return Object.isNumber(expression) ? Element.descendants(element)[expression] : + Element.select(element, expression)[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = Element.previousSiblings(element); + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = Element.nextSiblings(element); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + + select: function(element) { + var args = Array.prototype.slice.call(arguments, 1); + return Selector.findChildElements(element, args); + }, + + adjacent: function(element) { + var args = Array.prototype.slice.call(arguments, 1); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id)); + Element.writeAttribute(element, 'id', id); + return id; + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + var t = Element._attributeTranslations.read; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + if (name.include(':')) { + return (!element.attributes || !element.attributes[name]) ? null : + element.attributes[name].value; + } + } + return element.getAttribute(name); + }, + + writeAttribute: function(element, name, value) { + element = $(element); + var attributes = { }, t = Element._attributeTranslations.write; + + if (typeof name == 'object') attributes = name; + else attributes[name] = Object.isUndefined(value) ? true : value; + + for (var attr in attributes) { + name = t.names[attr] || attr; + value = attributes[attr]; + if (t.values[attr]) name = t.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + return element; + }, + + getHeight: function(element) { + return Element.getDimensions(element).height; + }, + + getWidth: function(element) { + return Element.getDimensions(element).width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + return (elementClassName.length > 0 && (elementClassName == className || + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + if (!Element.hasClassName(element, className)) + element.className += (element.className ? ' ' : '') + className; + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + element.className = element.className.replace( + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + return Element[Element.hasClassName(element, className) ? + 'removeClassName' : 'addClassName'](element, className); + }, + + 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); + + if (element.compareDocumentPosition) + return (element.compareDocumentPosition(ancestor) & 8) === 8; + + if (ancestor.contains) + return ancestor.contains(element) && ancestor !== element; + + while (element = element.parentNode) + if (element == ancestor) return true; + + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = Element.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 || value == 'auto') { + 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) { + element = $(element); + var elementStyle = element.style, match; + if (Object.isString(styles)) { + element.style.cssText += ';' + styles; + return styles.include('opacity') ? + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; + } + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]); + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : + property] = 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(element, 'display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari + 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'; + if (Prototype.Browser.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.getStyle(element, 'overflow') || 'auto'; + if (element._overflow !== '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; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return Element._returnOffset(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.toUpperCase() == 'BODY') break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + absolutize: function(element) { + element = $(element); + if (Element.getStyle(element, 'position') == 'absolute') return element; + + var offsets = Element.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'; + return element; + }, + + relativize: function(element) { + element = $(element); + if (Element.getStyle(element, 'position') == 'relative') return element; + + 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; + return element; + }, + + cumulativeScrollOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + getOffsetParent: 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); + }, + + viewportOffset: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + if (element.offsetParent == document.body && + Element.getStyle(element, 'position') == 'absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return Element._returnOffset(valueL, valueT); + }, + + clonePosition: function(element, source) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || { }); + + source = $(source); + var p = Element.viewportOffset(source); + + element = $(element); + var delta = [0, 0]; + var parent = null; + if (Element.getStyle(element, 'position') == 'absolute') { + parent = Element.getOffsetParent(element); + delta = Element.viewportOffset(parent); + } + + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if (options.setWidth) element.style.width = source.offsetWidth + 'px'; + if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + return element; + } +}; + +Object.extend(Element.Methods, { + getElementsBySelector: Element.Methods.select, + + childElements: Element.Methods.immediateDescendants +}); + +Element._attributeTranslations = { + write: { + names: { + className: 'class', + htmlFor: 'for' + }, + values: { } + } +}; + +if (Prototype.Browser.Opera) { + Element.Methods.getStyle = Element.Methods.getStyle.wrap( + function(proceed, element, style) { + switch (style) { + case 'left': case 'top': case 'right': case 'bottom': + if (proceed(element, 'position') === 'static') return null; + case 'height': case 'width': + if (!Element.visible(element)) return null; + + var dim = parseInt(proceed(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + var properties; + if (style === 'height') { + properties = ['border-top-width', 'padding-top', + 'padding-bottom', 'border-bottom-width']; + } + else { + properties = ['border-left-width', 'padding-left', + 'padding-right', 'border-right-width']; + } + return properties.inject(dim, function(memo, property) { + var val = proceed(element, property); + return val === null ? memo : memo - parseInt(val, 10); + }) + 'px'; + default: return proceed(element, style); + } + } + ); + + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( + function(proceed, element, attribute) { + if (attribute === 'title') return element.title; + return proceed(element, attribute); + } + ); +} + +else if (Prototype.Browser.IE) { + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + try { element.offsetParent } + catch(e) { return $(document.body) } + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { + Element.Methods[method] = Element.Methods[method].wrap( + function(proceed, element) { + element = $(element); + try { element.offsetParent } + catch(e) { return Element._returnOffset(0,0) } + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + }); + + Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( + function(proceed, element) { + try { element.offsetParent } + catch(e) { return Element._returnOffset(0,0) } + return proceed(element); + } + ); + + 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) { + function stripAlpha(filter){ + return filter.replace(/alpha\([^\)]*\)/gi,''); + } + element = $(element); + var currentStyle = element.currentStyle; + if ((currentStyle && !currentStyle.hasLayout) || + (!currentStyle && element.style.zoom == 'normal')) + element.style.zoom = 1; + + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + (filter = stripAlpha(filter)) ? + style.filter = filter : style.removeAttribute('filter'); + return element; + } else if (value < 0.00001) value = 0; + style.filter = stripAlpha(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + Element._attributeTranslations = (function(){ + + var classProp = 'className'; + var forProp = 'for'; + + var el = document.createElement('div'); + + el.setAttribute(classProp, 'x'); + + if (el.className !== 'x') { + el.setAttribute('class', 'x'); + if (el.className === 'x') { + classProp = 'class'; + } + } + el = null; + + el = document.createElement('label'); + el.setAttribute(forProp, 'x'); + if (el.htmlFor !== 'x') { + el.setAttribute('htmlFor', 'x'); + if (el.htmlFor === 'x') { + forProp = 'htmlFor'; + } + } + el = null; + + return { + read: { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute); + }, + _getAttr2: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: (function(){ + + var el = document.createElement('div'); + el.onclick = Prototype.emptyFunction; + var value = el.getAttribute('onclick'); + var f; + + if (String(value).indexOf('{') > -1) { + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + attribute = attribute.toString(); + attribute = attribute.split('{')[1]; + attribute = attribute.split('}')[0]; + return attribute.strip(); + }; + } + else if (value === '') { + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + return attribute.strip(); + }; + } + el = null; + return f; + })(), + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + } + } + })(); + + Element._attributeTranslations.write = { + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + Element._attributeTranslations.has = {}; + + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + + 'encType maxLength readOnly longDesc frameBorder').each(function(attr) { + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; + Element._attributeTranslations.has[attr.toLowerCase()] = attr; + }); + + (function(v) { + Object.extend(v, { + href: v._getAttr2, + src: v._getAttr2, + type: v._getAttr, + action: v._getAttrNode, + disabled: v._flag, + checked: v._flag, + readonly: v._flag, + multiple: v._flag, + onload: v._getEv, + onunload: v._getEv, + onclick: v._getEv, + ondblclick: v._getEv, + onmousedown: v._getEv, + onmouseup: v._getEv, + onmouseover: v._getEv, + onmousemove: v._getEv, + onmouseout: v._getEv, + onfocus: v._getEv, + onblur: v._getEv, + onkeypress: v._getEv, + onkeydown: v._getEv, + onkeyup: v._getEv, + onsubmit: v._getEv, + onreset: v._getEv, + onselect: v._getEv, + onchange: v._getEv + }); + })(Element._attributeTranslations.read.values); + + if (Prototype.BrowserFeatures.ElementExtensions) { + (function() { + function _descendants(element) { + var nodes = element.getElementsByTagName('*'), results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName !== "!") // Filter out comment nodes. + results.push(node); + return results; + } + + Element.Methods.down = function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? _descendants(element)[expression] : + Element.select(element, expression)[index || 0]; + } + })(); + } + +} + +else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +else if (Prototype.Browser.WebKit) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + + if (value == 1) + if(element.tagName.toUpperCase() == 'IMG' && element.width) { + element.width++; element.width--; + } else try { + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch (e) { } + + return element; + }; + + Element.Methods.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 Element._returnOffset(valueL, valueT); + }; +} + +if ('outerHTML' in document.documentElement) { + Element.Methods.replace = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (Element._insertionTranslations.tags[tagName]) { + var nextSibling = element.next(); + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + parent.removeChild(element); + if (nextSibling) + fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); + else + fragments.each(function(node) { parent.appendChild(node) }); + } + else element.outerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +Element._returnOffset = function(l, t) { + var result = [l, t]; + result.left = l; + result.top = t; + return result; +}; + +Element._getContentFromAnonymousElement = function(tagName, html) { + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; + return $A(div.childNodes); +}; + +Element._insertionTranslations = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + tags: { + TABLE: ['', '
        ', 1], + TBODY: ['', '
        ', 2], + TR: ['', '
        ', 3], + TD: ['
        ', '
        ', 4], + SELECT: ['', 1] + } +}; + +(function() { + var tags = Element._insertionTranslations.tags; + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); +})(); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + attribute = Element._attributeTranslations.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return !!(node && node.specified); + } +}; + +Element.Methods.ByTag = { }; + +Object.extend(Element, Element.Methods); + +(function(div) { + + if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) { + window.HTMLElement = { }; + window.HTMLElement.prototype = div['__proto__']; + Prototype.BrowserFeatures.ElementExtensions = true; + } + + div = null; + +})(document.createElement('div')) + +Element.extend = (function() { + + function checkDeficiency(tagName) { + if (typeof window.Element != 'undefined') { + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random()+'').slice(2); + var el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + } + return false; + } + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object'); + + if (Prototype.BrowserFeatures.SpecificElementExtensions) { + if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) { + return function(element) { + if (element && typeof element._extendedByPrototype == 'undefined') { + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + } + return element; + } + } + return Prototype.K; + } + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || typeof element._extendedByPrototype != 'undefined' || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + if (!Prototype.BrowserFeatures.ElementExtensions) { + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + } + } + }); + + extend.refresh(); + return extend; +})(); + +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 (Object.isArray(tagName)) 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; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + 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]; + + var element = document.createElement(tagName); + var proto = element['__proto__'] || element.constructor.prototype; + element = null; + return proto; + } + + var elementPrototype = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + copy(Element.Methods, elementPrototype); + copy(Element.Methods.Simulated, elementPrototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; + + if (Element.extend.refresh) Element.extend.refresh(); + Element.cache = { }; +}; + + +document.viewport = { + + getDimensions: function() { + return { width: this.getWidth(), height: this.getHeight() }; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; + +(function(viewport) { + var B = Prototype.Browser, doc = document, element, property = {}; + + function getRootElement() { + if (B.WebKit && !doc.evaluate) + return document; + + if (B.Opera && window.parseFloat(window.opera.version()) < 9.5) + return document.body; + + return document.documentElement; + } + + function define(D) { + if (!element) element = getRootElement(); + + property[D] = 'client' + D; + + viewport['get' + D] = function() { return element[property[D]] }; + return viewport['get' + D](); + } + + viewport.getWidth = define.curry('Width'); + + viewport.getHeight = define.curry('Height'); +})(document.viewport); + + +Element.Storage = { + UID: 1 +}; + +Element.addMethods({ + getStorage: function(element) { + if (!(element = $(element))) return; + + var uid; + if (element === window) { + uid = 0; + } else { + if (typeof element._prototypeUID === "undefined") + element._prototypeUID = [Element.Storage.UID++]; + uid = element._prototypeUID[0]; + } + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + }, + + store: function(element, key, value) { + if (!(element = $(element))) return; + + if (arguments.length === 2) { + Element.getStorage(element).update(key); + } else { + Element.getStorage(element).set(key, value); + } + + return element; + }, + + retrieve: function(element, key, defaultValue) { + if (!(element = $(element))) return; + var hash = Element.getStorage(element), value = hash.get(key); + + if (Object.isUndefined(value)) { + hash.set(key, defaultValue); + value = defaultValue; + } + + return value; + }, + + clone: function(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + clone._prototypeUID = void 0; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) { + descendants[i]._prototypeUID = void 0; + } + } + return Element.extend(clone); + } +}); +/* 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({ + initialize: function(expression) { + this.expression = expression.strip(); + + if (this.shouldUseSelectorsAPI()) { + this.mode = 'selectorsAPI'; + } else if (this.shouldUseXPath()) { + this.mode = 'xpath'; + this.compileXPathMatcher(); + } else { + this.mode = "normal"; + this.compileMatcher(); + } + + }, + + shouldUseXPath: (function() { + + var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ + var isBuggy = false; + if (document.evaluate && window.XPathResult) { + var el = document.createElement('div'); + el.innerHTML = '
        '; + + var xpath = ".//*[local-name()='ul' or local-name()='UL']" + + "//*[local-name()='li' or local-name()='LI']"; + + var result = document.evaluate(xpath, el, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + + isBuggy = (result.snapshotLength !== 2); + el = null; + } + return isBuggy; + })(); + + return function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + if ((/(\[[\w-]*?:|:checked)/).test(e)) + return false; + + if (IS_DESCENDANT_SELECTOR_BUGGY) return false; + + return true; + } + + })(), + + shouldUseSelectorsAPI: function() { + if (!Prototype.BrowserFeatures.SelectorsAPI) return false; + + if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; + + if (!Selector._div) Selector._div = new Element('div'); + + try { + Selector._div.querySelector(this.expression); + } catch(e) { + return false; + } + + return true; + }, + + compileMatcher: function() { + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m, len = ps.length, name; + + 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 = 0; i"; + } +}); + +if (Prototype.BrowserFeatures.SelectorsAPI && + document.compatMode === 'BackCompat') { + Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ + var div = document.createElement('div'), + span = document.createElement('span'); + + div.id = "prototype_test_id"; + span.className = 'Test'; + div.appendChild(span); + var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); + div = span = null; + return isIgnored; + })(); +} + +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: function(m) { + m[1] = m[1].toLowerCase(); + return new Template("[@#{1}]").evaluate(m); + }, + attr: function(m) { + m[1] = m[1].toLowerCase(); + 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 (Object.isFunction(h)) 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)]", + 'checked': "[@checked]", + 'disabled': "[(@disabled) and (@type!='hidden')]", + 'enabled': "[not(@disabled) and (@type!='hidden')]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, v, len = p.length, name; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i = 0; i= 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); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); 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: [ + { name: 'laterSibling', re: /^\s*~\s*/ }, + { name: 'child', re: /^\s*>\s*/ }, + { name: 'adjacent', re: /^\s*\+\s*/ }, + { name: 'descendant', re: /^\s/ }, + + { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, + { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, + { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, + { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, + { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, + { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } + ], + + assertions: { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }, + + handlers: { + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + mark: function(nodes) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: (function(){ + + var PROPERTIES_ATTRIBUTES_MAP = (function(){ + var el = document.createElement('div'), + isBuggy = false, + propName = '_countedByPrototype', + value = 'x' + el[propName] = value; + isBuggy = (el.getAttribute(propName) === value); + el = null; + return isBuggy; + })(); + + return PROPERTIES_ATTRIBUTES_MAP ? + function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } : + function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = void 0; + return nodes; + } + })(), + + index: function(parentNode, reverse, ofType) { + parentNode._countedByPrototype = Prototype.emptyFunction; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + var node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + }, + + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { + n._countedByPrototype = Prototype.emptyFunction; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + 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, 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; + }, + + tagName: function(nodes, root, tagName, combinator) { + var uTagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + 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() === uTagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + + if (root == document) { + if (!targetNode) return []; + if (!nodes) return [targetNode]; + } else { + if (!root.sourceIndex || root.sourceIndex < 1) { + var nodes = root.getElementsByTagName('*'); + for (var j = 0, node; node = nodes[j]; j++) { + if (node.id === id) return [node]; + } + } + } + + 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, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + 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, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + 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); + }, + + 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; + }); + }, + + 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._countedByPrototype) { + 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++) { + if (node.tagName == '!' || node.firstChild) 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._countedByPrototype) 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 && (!node.type || node.type !== 'hidden')) + 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 == v || nv && nv.startsWith(v); }, + '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, + '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + + '-').include('-' + (v || "").toUpperCase() + '-'); } + }, + + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + + matchElements: function(elements, expression) { + var matches = $$(expression), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._countedByPrototype) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (Object.isNumber(expression)) { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + expressions = Selector.split(expressions.join(',')); + 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; + } +}); + +if (Prototype.Browser.IE) { + Object.extend(Selector.handlers, { + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} + +var Form = { + reset: function(form) { + form = $(form); + form.reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit; + + var data = elements.inject({ }, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return options.hash ? data : Object.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + getElements: function(form) { + var elements = $(form).getElementsByTagName('*'), + element, + arr = [ ], + serializers = Form.Element.Serializers; + for (var i = 0; element = elements[i]; i++) { + arr.push(element); + } + return arr.inject([], function(elements, child) { + if (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) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return /^(?:input|select|textarea)$/i.test(element.tagName); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(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 Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return 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)$/i.test(element.type)))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + 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, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element, value); + default: + return Form.Element.Serializers.textarea(element, value); + } + }, + + inputSelector: function(element, value) { + if (Object.isUndefined(value)) return element.checked ? element.value : null; + else element.checked = !!value; + }, + + textarea: function(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + }, + + select: function(element, value) { + if (Object.isUndefined(value)) + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + else { + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; + } + } + else opt.selected = value.include(currentValue); + } + } + }, + + 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) { + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +}; + +/*--------------------------------------------------------------------------*/ + + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + 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, 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(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +(function() { + + var 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, + KEY_INSERT: 45, + + cache: {} + }; + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + var _isButton; + if (Prototype.Browser.IE) { + var buttonMap = { 0: 1, 1: 4, 2: 2 }; + _isButton = function(event, code) { + return event.button === buttonMap[code]; + }; + } else if (Prototype.Browser.WebKit) { + _isButton = function(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; + default: return false; + } + }; + } else { + _isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + + if (node.nodeType == Node.TEXT_NODE) + node = node.parentNode; + + return Element.extend(node); + } + + function findElement(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } + + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }); + + Event.extend = function(event, element) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + return Object.extend(event, methods); + }; + } else { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + Event.extend = Prototype.K; + } + + function _createResponder(element, eventName, handler) { + var registry = Element.retrieve(element, 'prototype_event_registry'); + + if (Object.isUndefined(registry)) { + CACHE.push(element); + registry = Element.retrieve(element, 'prototype_event_registry', $H()); + } + + var respondersForEvent = registry.get(eventName); + if (Object.isUndefined(respondersForEvent)) { + respondersForEvent = []; + registry.set(eventName, respondersForEvent); + } + + if (respondersForEvent.pluck('handler').include(handler)) return false; + + var responder; + if (eventName.include(":")) { + responder = function(event) { + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } else { + if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === "mouseenter" || eventName === "mouseleave")) { + if (eventName === "mouseenter" || eventName === "mouseleave") { + responder = function(event) { + Event.extend(event, element); + + var parent = event.relatedTarget; + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + + handler.call(element, event); + }; + } + } else { + responder = function(event) { + Event.extend(event, element); + handler.call(element, event); + }; + } + } + + responder.handler = handler; + respondersForEvent.push(responder); + return responder; + } + + function _destroyCache() { + for (var i = 0, length = CACHE.length; i < length; i++) { + Event.stopObserving(CACHE[i]); + CACHE[i] = null; + } + } + + var CACHE = []; + + if (Prototype.Browser.IE) + window.attachEvent('onunload', _destroyCache); + + if (Prototype.Browser.WebKit) + window.addEventListener('unload', Prototype.emptyFunction, false); + + + var _getDOMEventName = Prototype.K; + + if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { + _getDOMEventName = function(eventName) { + var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; + return eventName in translations ? translations[eventName] : eventName; + }; + } + + function observe(element, eventName, handler) { + element = $(element); + + var responder = _createResponder(element, eventName, handler); + + if (!responder) return element; + + if (eventName.include(':')) { + if (element.addEventListener) + element.addEventListener("dataavailable", responder, false); + else { + element.attachEvent("ondataavailable", responder); + element.attachEvent("onfilterchange", responder); + } + } else { + var actualEventName = _getDOMEventName(eventName); + + if (element.addEventListener) + element.addEventListener(actualEventName, responder, false); + else + element.attachEvent("on" + actualEventName, responder); + } + + return element; + } + + function stopObserving(element, eventName, handler) { + element = $(element); + + var registry = Element.retrieve(element, 'prototype_event_registry'); + + if (Object.isUndefined(registry)) return element; + + if (eventName && !handler) { + var responders = registry.get(eventName); + + if (Object.isUndefined(responders)) return element; + + responders.each( function(r) { + Element.stopObserving(element, eventName, r.handler); + }); + return element; + } else if (!eventName) { + registry.each( function(pair) { + var eventName = pair.key, responders = pair.value; + + responders.each( function(r) { + Element.stopObserving(element, eventName, r.handler); + }); + }); + return element; + } + + var responders = registry.get(eventName); + + if (!responders) return; + + var responder = responders.find( function(r) { return r.handler === handler; }); + if (!responder) return element; + + var actualEventName = _getDOMEventName(eventName); + + if (eventName.include(':')) { + if (element.removeEventListener) + element.removeEventListener("dataavailable", responder, false); + else { + element.detachEvent("ondataavailable", responder); + element.detachEvent("onfilterchange", responder); + } + } else { + if (element.removeEventListener) + element.removeEventListener(actualEventName, responder, false); + else + element.detachEvent('on' + actualEventName, responder); + } + + registry.set(eventName, responders.without(responder)); + + return element; + } + + function fire(element, eventName, memo, bubble) { + element = $(element); + + if (Object.isUndefined(bubble)) + bubble = true; + + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + + var event; + if (document.createEvent) { + event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', true, true); + } else { + event = document.createEventObject(); + event.eventType = bubble ? 'ondataavailable' : 'onfilterchange'; + } + + event.eventName = eventName; + event.memo = memo || { }; + + if (document.createEvent) + element.dispatchEvent(event); + else + element.fireEvent(event.eventType, event); + + return Event.extend(event); + } + + + Object.extend(Event, Event.Methods); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + loaded: false + }); + + if (window.Event) Object.extend(window.Event, Event); + else window.Event = Event; +})(); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearTimeout(timer); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.stopObserving('readystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { document.documentElement.doScroll('left'); } + catch(e) { + timer = pollDoScroll.defer(); + return; + } + fireContentLoadedEvent(); + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.observe('readystatechange', checkReadyState); + if (window == top) + timer = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(); + +Element.addMethods(); + +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.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 = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.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); + }, + + 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; + }, + + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +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); + +/*--------------------------------------------------------------------------*/ diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/robots.txt b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/robots.txt new file mode 100644 index 00000000000..085187fa58b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/html/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-Agent: * +# Disallow: / diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/code_statistics.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/code_statistics.rb new file mode 100644 index 00000000000..740d8a1767b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/code_statistics.rb @@ -0,0 +1,107 @@ +class CodeStatistics #:nodoc: + + TEST_TYPES = %w(Units Functionals Unit\ tests Functional\ tests Integration\ tests) + + def initialize(*pairs) + @pairs = pairs + @statistics = calculate_statistics + @total = calculate_total if pairs.length > 1 + end + + def to_s + print_header + @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) } + print_splitter + + if @total + print_line("Total", @total) + print_splitter + end + + print_code_test_stats + end + + private + def calculate_statistics + @pairs.inject({}) { |stats, pair| stats[pair.first] = calculate_directory_statistics(pair.last); stats } + end + + def calculate_directory_statistics(directory, pattern = /.*\.rb$/) + stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 } + + Dir.foreach(directory) do |file_name| + if File.stat(directory + "/" + file_name).directory? and (/^\./ !~ file_name) + newstats = calculate_directory_statistics(directory + "/" + file_name, pattern) + stats.each { |k, v| stats[k] += newstats[k] } + end + + next unless file_name =~ pattern + + f = File.open(directory + "/" + file_name) + + while line = f.gets + stats["lines"] += 1 + stats["classes"] += 1 if line =~ /class [A-Z]/ + stats["methods"] += 1 if line =~ /def [a-z]/ + stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/ + end + end + + stats + end + + def calculate_total + total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 } + @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } } + total + end + + def calculate_code + code_loc = 0 + @statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k } + code_loc + end + + def calculate_tests + test_loc = 0 + @statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k } + test_loc + end + + def print_header + print_splitter + puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |" + print_splitter + end + + def print_splitter + puts "+----------------------+-------+-------+---------+---------+-----+-------+" + end + + def print_line(name, statistics) + m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0 + loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0 + + start = if TEST_TYPES.include? name + "| #{name.ljust(20)} " + else + "| #{name.ljust(20)} " + end + + puts start + + "| #{statistics["lines"].to_s.rjust(5)} " + + "| #{statistics["codelines"].to_s.rjust(5)} " + + "| #{statistics["classes"].to_s.rjust(7)} " + + "| #{statistics["methods"].to_s.rjust(7)} " + + "| #{m_over_c.to_s.rjust(3)} " + + "| #{loc_over_m.to_s.rjust(5)} |" + end + + def print_code_test_stats + code = calculate_code + tests = calculate_tests + + puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}" + puts "" + end + end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands.rb new file mode 100644 index 00000000000..841e98a0dc0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands.rb @@ -0,0 +1,17 @@ +commands = Dir["#{File.dirname(__FILE__)}/commands/*.rb"].collect { |file_path| File.basename(file_path).split(".").first } + +if commands.include?(ARGV.first) + require "#{File.dirname(__FILE__)}/commands/#{ARGV.shift}" +else + puts <<-USAGE +The 'run' provides a unified access point for all the default Rails' commands. + +Usage: ./script/run [OPTIONS] + +Examples: + ./script/run generate controller Admin + ./script/run process reaper + +USAGE + puts "Choose: #{commands.join(", ")}" +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/about.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/about.rb new file mode 100644 index 00000000000..bc2cfcb948c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/about.rb @@ -0,0 +1,3 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails/info' +puts Rails::Info diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/console.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/console.rb new file mode 100644 index 00000000000..63df8346396 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/console.rb @@ -0,0 +1,45 @@ +irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb' + +require 'optparse' + +options = { :sandbox => false, :irb => irb } +OptionParser.new do |opt| + opt.banner = "Usage: console [environment] [options]" + opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v } + opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v } + opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v } + opt.parse!(ARGV) +end + +libs = " -r irb/completion" +libs << %( -r "#{RAILS_ROOT}/config/environment") +libs << " -r console_app" +libs << " -r console_sandbox" if options[:sandbox] +libs << " -r console_with_helpers" + +if options[:debugger] + begin + require 'ruby-debug' + libs << " -r ruby-debug" + puts "=> Debugger enabled" + rescue Exception + puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'" + exit + end +end + +ENV['RAILS_ENV'] = case ARGV.first + when "p"; "production" + when "d"; "development" + when "t"; "test" + else + ARGV.first || ENV['RAILS_ENV'] || 'development' +end + +if options[:sandbox] + puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})" + puts "Any modifications you make will be rolled back on exit" +else + puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})" +end +exec "#{options[:irb]} #{libs} --simple-prompt" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/dbconsole.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/dbconsole.rb new file mode 100644 index 00000000000..e6f11a45db6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/dbconsole.rb @@ -0,0 +1,87 @@ +require 'erb' +require 'yaml' +require 'optparse' + +include_password = false +options = {} + +OptionParser.new do |opt| + opt.banner = "Usage: dbconsole [options] [environment]" + opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| + include_password = true + end + + opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], + "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| + options['mode'] = mode + end + + opt.on("-h", "--header") do |h| + options['header'] = h + end + + opt.parse!(ARGV) + abort opt.to_s unless (0..1).include?(ARGV.size) +end + +env = ARGV.first || ENV['RAILS_ENV'] || 'development' +unless config = YAML::load(ERB.new(IO.read(RAILS_ROOT + "/config/database.yml")).result)[env] + abort "No database is configured for the environment '#{env}'" +end + + +def find_cmd(*commands) + dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) + commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ + + full_path_command = nil + found = commands.detect do |cmd| + dir = dirs_on_path.detect do |path| + full_path_command = File.join(path, cmd) + File.executable? full_path_command + end + end + found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") +end + +case config["adapter"] +when "mysql" + args = { + 'host' => '--host', + 'port' => '--port', + 'socket' => '--socket', + 'username' => '--user', + 'encoding' => '--default-character-set' + }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact + + if config['password'] && include_password + args << "--password=#{config['password']}" + elsif config['password'] && !config['password'].to_s.empty? + args << "-p" + end + + args << config['database'] + + exec(find_cmd('mysql', 'mysql5'), *args) + +when "postgresql" + ENV['PGUSER'] = config["username"] if config["username"] + ENV['PGHOST'] = config["host"] if config["host"] + ENV['PGPORT'] = config["port"].to_s if config["port"] + ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password + exec(find_cmd('psql'), config["database"]) + +when "sqlite" + exec(find_cmd('sqlite'), config["database"]) + +when "sqlite3" + args = [] + + args << "-#{options['mode']}" if options['mode'] + args << "-header" if options['header'] + args << config['database'] + + exec(find_cmd('sqlite3'), *args) +else + abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/destroy.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/destroy.rb new file mode 100644 index 00000000000..f4b81d65117 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/destroy.rb @@ -0,0 +1,6 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails_generator' +require 'rails_generator/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +Rails::Generator::Scripts::Destroy.new.run(ARGV) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/generate.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/generate.rb new file mode 100644 index 00000000000..3d3db3d8565 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/generate.rb @@ -0,0 +1,6 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails_generator' +require 'rails_generator/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +Rails::Generator::Scripts::Generate.new.run(ARGV) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/ncgi/listener b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/ncgi/listener new file mode 100644 index 00000000000..7079ef78a6e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/ncgi/listener @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby + +require 'stringio' +require 'fileutils' +require 'fcgi_handler' + +def message(s) + $stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"] +end + +class RemoteCGI < CGI + attr_accessor :stdinput, :stdoutput, :env_table + def initialize(env_table, input = nil, output = nil) + self.env_table = env_table + self.stdinput = input || StringIO.new + self.stdoutput = output || StringIO.new + super() + end + + def out(stream) # Ignore the requested output stream + super(stdoutput) + end +end + +class Listener + include DRbUndumped + + def initialize(timeout, socket_path) + @socket = File.expand_path(socket_path) + @mutex = Mutex.new + @active = false + @timeout = timeout + + @handler = RailsFCGIHandler.new + @handler.extend DRbUndumped + + message 'opening socket' + DRb.start_service("drbunix:#{@socket}", self) + + message 'entering process loop' + @handler.process! self + end + + def each_cgi(&cgi_block) + @cgi_block = cgi_block + message 'entering idle loop' + loop do + sleep @timeout rescue nil + die! unless @active + @active = false + end + end + + def process(env, input) + message 'received request' + @mutex.synchronize do + @active = true + + message 'creating input stream' + input_stream = StringIO.new(input) + message 'building CGI instance' + cgi = RemoteCGI.new(eval(env), input_stream) + + message 'yielding to fcgi handler' + @cgi_block.call cgi + message 'yield finished -- sending output' + + cgi.stdoutput.seek(0) + output = cgi.stdoutput.read + + return output + end + end + + def die! + message 'shutting down' + DRb.stop_service + FileUtils.rm_f @socket + Kernel.exit 0 + end +end + +socket_path = ARGV.shift +timeout = (ARGV.shift || 90).to_i + +Listener.new(timeout, socket_path) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/ncgi/tracker b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/ncgi/tracker new file mode 100644 index 00000000000..4ca12d779b6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/ncgi/tracker @@ -0,0 +1,69 @@ +#!/usr/bin/env ruby + +require 'drb' +require 'thread' + +def message(s) + $stderr.puts "tracker: #{s}" if ENV && ENV["DEBUG_GATEWAY"] +end + +class Tracker + include DRbUndumped + + def initialize(instances, socket_path) + @instances = instances + @socket = File.expand_path(socket_path) + @active = false + + @listeners = [] + @instances.times { @listeners << Mutex.new } + + message "using #{@listeners.length} listeners" + message "opening socket at #{@socket}" + + @service = DRb.start_service("drbunix://#{@socket}", self) + end + + def with_listener + message "listener requested" + + mutex = has_lock = index = nil + 3.times do + @listeners.each_with_index do |mutex, index| + has_lock = mutex.try_lock + break if has_lock + end + break if has_lock + sleep 0.05 + end + + if has_lock + message "obtained listener #{index}" + @active = true + begin yield index + ensure + mutex.unlock + message "released listener #{index}" + end + else + message "dropping request because no listeners are available!" + end + end + + def background(check_interval = nil) + if check_interval + loop do + sleep check_interval + message "Idle for #{check_interval}, shutting down" unless @active + @active = false + Kernel.exit 0 + end + else DRb.thread.join + end + end +end + +socket_path = ARGV.shift +instances = ARGV.shift.to_i +t = Tracker.new(instances, socket_path) +t.background(ARGV.first ? ARGV.shift.to_i : 90) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/performance/benchmarker.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/performance/benchmarker.rb new file mode 100644 index 00000000000..e8804fe1bfd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/performance/benchmarker.rb @@ -0,0 +1,24 @@ +if ARGV.empty? + puts "Usage: ./script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..." + exit 1 +end + +begin + N = Integer(ARGV.first) + ARGV.shift +rescue ArgumentError + N = 1 +end + +require RAILS_ROOT + '/config/environment' +require 'benchmark' +include Benchmark + +# Don't include compilation in the benchmark +ARGV.each { |expression| eval(expression) } + +bm(6) do |x| + ARGV.each_with_index do |expression, idx| + x.report("##{idx + 1}") { N.times { eval(expression) } } + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/performance/profiler.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/performance/profiler.rb new file mode 100644 index 00000000000..7df840f1974 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/performance/profiler.rb @@ -0,0 +1,50 @@ +if ARGV.empty? + $stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]" + exit(1) +end + +# Keep the expensive require out of the profile. +$stderr.puts 'Loading Rails...' +require RAILS_ROOT + '/config/environment' + +# Define a method to profile. +if ARGV[1] and ARGV[1].to_i > 1 + eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end" +else + eval "def profile_me() #{ARGV[0]} end" +end + +# Use the ruby-prof extension if available. Fall back to stdlib profiler. +begin + begin + require "ruby-prof" + $stderr.puts 'Using the ruby-prof extension.' + RubyProf.measure_mode = RubyProf::WALL_TIME + RubyProf.start + profile_me + results = RubyProf.stop + if ARGV[2] + printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify) + else + printer_class = RubyProf::FlatPrinter + end + printer = printer_class.new(results) + printer.print($stderr) + rescue LoadError + require "prof" + $stderr.puts 'Using the old ruby-prof extension.' + Prof.clock_mode = Prof::GETTIMEOFDAY + Prof.start + profile_me + results = Prof.stop + require 'rubyprof_ext' + Prof.print_profile(results, $stderr) + end +rescue LoadError + require 'profiler' + $stderr.puts 'Using the standard Ruby profiler.' + Profiler__.start_profile + profile_me + Profiler__.stop_profile + Profiler__.print_profile($stderr) +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/plugin.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/plugin.rb new file mode 100644 index 00000000000..9f99a8d5575 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/plugin.rb @@ -0,0 +1,968 @@ +# Rails Plugin Manager. +# +# Listing available plugins: +# +# $ ./script/plugin list +# continuous_builder http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder +# asset_timestamping http://svn.aviditybytes.com/rails/plugins/asset_timestamping +# enumerations_mixin http://svn.protocool.com/rails/plugins/enumerations_mixin/trunk +# calculations http://techno-weenie.net/svn/projects/calculations/ +# ... +# +# Installing plugins: +# +# $ ./script/plugin install continuous_builder asset_timestamping +# +# Finding Repositories: +# +# $ ./script/plugin discover +# +# Adding Repositories: +# +# $ ./script/plugin source http://svn.protocool.com/rails/plugins/ +# +# How it works: +# +# * Maintains a list of subversion repositories that are assumed to have +# a plugin directory structure. Manage them with the (source, unsource, +# and sources commands) +# +# * The discover command scrapes the following page for things that +# look like subversion repositories with plugins: +# http://wiki.rubyonrails.org/rails/pages/Plugins +# +# * Unless you specify that you want to use svn, script/plugin uses plain old +# HTTP for downloads. The following bullets are true if you specify +# that you want to use svn. +# +# * If `vendor/plugins` is under subversion control, the script will +# modify the svn:externals property and perform an update. You can +# use normal subversion commands to keep the plugins up to date. +# +# * Or, if `vendor/plugins` is not under subversion control, the +# plugin is pulled via `svn checkout` or `svn export` but looks +# exactly the same. +# +# Specifying revisions: +# +# * Subversion revision is a single integer. +# +# * Git revision format: +# - full - 'refs/tags/1.8.0' or 'refs/heads/experimental' +# - short: 'experimental' (equivalent to 'refs/heads/experimental') +# 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0') +# +# +# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com) +# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php) + +$verbose = false + + +require 'open-uri' +require 'fileutils' +require 'tempfile' + +include FileUtils + +class RailsEnvironment + attr_reader :root + + def initialize(dir) + @root = dir + end + + def self.find(dir=nil) + dir ||= pwd + while dir.length > 1 + return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb')) + dir = File.dirname(dir) + end + end + + def self.default + @default ||= find + end + + def self.default=(rails_env) + @default = rails_env + end + + def install(name_uri_or_plugin) + if name_uri_or_plugin.is_a? String + if name_uri_or_plugin =~ /:\/\// + plugin = Plugin.new(name_uri_or_plugin) + else + plugin = Plugins[name_uri_or_plugin] + end + else + plugin = name_uri_or_plugin + end + unless plugin.nil? + plugin.install + else + puts "Plugin not found: #{name_uri_or_plugin}" + end + end + + def use_svn? + require 'active_support/core_ext/kernel' + silence_stderr {`svn --version` rescue nil} + !$?.nil? && $?.success? + end + + def use_externals? + use_svn? && File.directory?("#{root}/vendor/plugins/.svn") + end + + def use_checkout? + # this is a bit of a guess. we assume that if the rails environment + # is under subversion then they probably want the plugin checked out + # instead of exported. This can be overridden on the command line + File.directory?("#{root}/.svn") + end + + def best_install_method + return :http unless use_svn? + case + when use_externals? then :externals + when use_checkout? then :checkout + else :export + end + end + + def externals + return [] unless use_externals? + ext = `svn propget svn:externals "#{root}/vendor/plugins"` + lines = ext.respond_to?(:lines) ? ext.lines : ext + lines.reject{ |line| line.strip == '' }.map do |line| + line.strip.split(/\s+/, 2) + end + end + + def externals=(items) + unless items.is_a? String + items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n") + end + Tempfile.open("svn-set-prop") do |file| + file.write(items) + file.flush + system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"") + end + end + +end + +class Plugin + attr_reader :name, :uri + + def initialize(uri, name=nil) + @uri = uri + guess_name(uri) + end + + def self.find(name) + name =~ /\// ? new(name) : Repositories.instance.find_plugin(name) + end + + def to_s + "#{@name.ljust(30)}#{@uri}" + end + + def svn_url? + @uri =~ /svn(?:\+ssh)?:\/\/*/ + end + + def git_url? + @uri =~ /^git:\/\// || @uri =~ /\.git$/ + end + + def installed? + File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \ + or rails_env.externals.detect{ |name, repo| self.uri == repo } + end + + def install(method=nil, options = {}) + method ||= rails_env.best_install_method? + if :http == method + method = :export if svn_url? + method = :git if git_url? + end + + uninstall if installed? and options[:force] + + unless installed? + send("install_using_#{method}", options) + run_install_hook + else + puts "already installed: #{name} (#{uri}). pass --force to reinstall" + end + end + + def uninstall + path = "#{rails_env.root}/vendor/plugins/#{name}" + if File.directory?(path) + puts "Removing 'vendor/plugins/#{name}'" if $verbose + run_uninstall_hook + rm_r path + else + puts "Plugin doesn't exist: #{path}" + end + # clean up svn:externals + externals = rails_env.externals + externals.reject!{|n,u| name == n or name == u} + rails_env.externals = externals + end + + def info + tmp = "#{rails_env.root}/_tmp_about.yml" + if svn_url? + cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\"" + puts cmd if $verbose + system(cmd) + end + open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream| + stream.read + end rescue "No about.yml found in #{uri}" + ensure + FileUtils.rm_rf tmp if svn_url? + end + + private + + def run_install_hook + install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb" + load install_hook_file if File.exist? install_hook_file + end + + def run_uninstall_hook + uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb" + load uninstall_hook_file if File.exist? uninstall_hook_file + end + + def install_using_export(options = {}) + svn_command :export, options + end + + def install_using_checkout(options = {}) + svn_command :checkout, options + end + + def install_using_externals(options = {}) + externals = rails_env.externals + externals.push([@name, uri]) + rails_env.externals = externals + install_using_checkout(options) + end + + def install_using_http(options = {}) + root = rails_env.root + mkdir_p "#{root}/vendor/plugins/#{@name}" + Dir.chdir "#{root}/vendor/plugins/#{@name}" do + puts "fetching from '#{uri}'" if $verbose + fetcher = RecursiveHTTPFetcher.new(uri, -1) + fetcher.quiet = true if options[:quiet] + fetcher.fetch + end + end + + def install_using_git(options = {}) + root = rails_env.root + mkdir_p(install_path = "#{root}/vendor/plugins/#{name}") + Dir.chdir install_path do + init_cmd = "git init" + init_cmd += " -q" if options[:quiet] and not $verbose + puts init_cmd if $verbose + system(init_cmd) + base_cmd = "git pull --depth 1 #{uri}" + base_cmd += " -q" if options[:quiet] and not $verbose + base_cmd += " #{options[:revision]}" if options[:revision] + puts base_cmd if $verbose + if system(base_cmd) + puts "removing: .git .gitignore" if $verbose + rm_rf %w(.git .gitignore) + else + rm_rf install_path + end + end + end + + def svn_command(cmd, options = {}) + root = rails_env.root + mkdir_p "#{root}/vendor/plugins" + base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\"" + base_cmd += ' -q' if options[:quiet] and not $verbose + base_cmd += " -r #{options[:revision]}" if options[:revision] + puts base_cmd if $verbose + system(base_cmd) + end + + def guess_name(url) + @name = File.basename(url) + if @name == 'trunk' || @name.empty? + @name = File.basename(File.dirname(url)) + end + @name.gsub!(/\.git$/, '') if @name =~ /\.git$/ + end + + def rails_env + @rails_env || RailsEnvironment.default + end +end + +class Repositories + include Enumerable + + def initialize(cache_file = File.join(find_home, ".rails-plugin-sources")) + @cache_file = File.expand_path(cache_file) + load! + end + + def each(&block) + @repositories.each(&block) + end + + def add(uri) + unless find{|repo| repo.uri == uri } + @repositories.push(Repository.new(uri)).last + end + end + + def remove(uri) + @repositories.reject!{|repo| repo.uri == uri} + end + + def exist?(uri) + @repositories.detect{|repo| repo.uri == uri } + end + + def all + @repositories + end + + def find_plugin(name) + @repositories.each do |repo| + repo.each do |plugin| + return plugin if plugin.name == name + end + end + return nil + end + + def load! + contents = File.exist?(@cache_file) ? File.read(@cache_file) : defaults + contents = defaults if contents.empty? + @repositories = contents.split(/\n/).reject do |line| + line =~ /^\s*#/ or line =~ /^\s*$/ + end.map { |source| Repository.new(source.strip) } + end + + def save + File.open(@cache_file, 'w') do |f| + each do |repo| + f.write(repo.uri) + f.write("\n") + end + end + end + + def defaults + <<-DEFAULTS + http://dev.rubyonrails.com/svn/rails/plugins/ + DEFAULTS + end + + def find_home + ['HOME', 'USERPROFILE'].each do |homekey| + return ENV[homekey] if ENV[homekey] + end + if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] + return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" + end + begin + File.expand_path("~") + rescue StandardError => ex + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + + def self.instance + @instance ||= Repositories.new + end + + def self.each(&block) + self.instance.each(&block) + end +end + +class Repository + include Enumerable + attr_reader :uri, :plugins + + def initialize(uri) + @uri = uri.chomp('/') << "/" + @plugins = nil + end + + def plugins + unless @plugins + if $verbose + puts "Discovering plugins in #{@uri}" + puts index + end + + @plugins = index.reject{ |line| line !~ /\/$/ } + @plugins.map! { |name| Plugin.new(File.join(@uri, name), name) } + end + + @plugins + end + + def each(&block) + plugins.each(&block) + end + + private + def index + @index ||= RecursiveHTTPFetcher.new(@uri).ls + end +end + + +# load default environment and parse arguments +require 'optparse' +module Commands + + class Plugin + attr_reader :environment, :script_name, :sources + def initialize + @environment = RailsEnvironment.default + @rails_root = RailsEnvironment.default.root + @script_name = File.basename($0) + @sources = [] + end + + def environment=(value) + @environment = value + RailsEnvironment.default = value + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@script_name} [OPTIONS] command" + o.define_head "Rails plugin manager." + + o.separator "" + o.separator "GENERAL OPTIONS" + + o.on("-r", "--root=DIR", String, + "Set an explicit rails app directory.", + "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) } + o.on("-s", "--source=URL1,URL2", Array, + "Use the specified plugin repositories instead of the defaults.") { |sources| @sources = sources} + + o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose } + o.on("-h", "--help", "Show this help message.") { puts o; exit } + + o.separator "" + o.separator "COMMANDS" + + o.separator " discover Discover plugin repositories." + o.separator " list List available plugins." + o.separator " install Install plugin(s) from known repositories or URLs." + o.separator " update Update installed plugins." + o.separator " remove Uninstall plugins." + o.separator " source Add a plugin source repository." + o.separator " unsource Remove a plugin repository." + o.separator " sources List currently configured plugin repositories." + + o.separator "" + o.separator "EXAMPLES" + o.separator " Install a plugin:" + o.separator " #{@script_name} install continuous_builder\n" + o.separator " Install a plugin from a subversion URL:" + o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n" + o.separator " Install a plugin from a git URL:" + o.separator " #{@script_name} install git://github.com/SomeGuy/my_awesome_plugin.git\n" + o.separator " Install a plugin and add a svn:externals entry to vendor/plugins" + o.separator " #{@script_name} install -x continuous_builder\n" + o.separator " List all available plugins:" + o.separator " #{@script_name} list\n" + o.separator " List plugins in the specified repository:" + o.separator " #{@script_name} list --source=http://dev.rubyonrails.com/svn/rails/plugins/\n" + o.separator " Discover and prompt to add new repositories:" + o.separator " #{@script_name} discover\n" + o.separator " Discover new repositories but just list them, don't add anything:" + o.separator " #{@script_name} discover -l\n" + o.separator " Add a new repository to the source list:" + o.separator " #{@script_name} source http://dev.rubyonrails.com/svn/rails/plugins/\n" + o.separator " Remove a repository from the source list:" + o.separator " #{@script_name} unsource http://dev.rubyonrails.com/svn/rails/plugins/\n" + o.separator " Show currently configured repositories:" + o.separator " #{@script_name} sources\n" + end + end + + def parse!(args=ARGV) + general, sub = split_args(args) + options.parse!(general) + + command = general.shift + if command =~ /^(list|discover|install|source|unsource|sources|remove|update|info)$/ + command = Commands.const_get(command.capitalize).new(self) + command.parse!(sub) + else + puts "Unknown command: #{command}" + puts options + exit 1 + end + end + + def split_args(args) + left = [] + left << args.shift while args[0] and args[0] =~ /^-/ + left << args.shift if args[0] + return [left, args] + end + + def self.parse!(args=ARGV) + Plugin.new.parse!(args) + end + end + + + class List + def initialize(base_command) + @base_command = base_command + @sources = [] + @local = false + @remote = true + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} list [OPTIONS] [PATTERN]" + o.define_head "List available plugins." + o.separator "" + o.separator "Options:" + o.separator "" + o.on( "-s", "--source=URL1,URL2", Array, + "Use the specified plugin repositories.") {|sources| @sources = sources} + o.on( "--local", + "List locally installed plugins.") {|local| @local, @remote = local, false} + o.on( "--remote", + "List remotely available plugins. This is the default behavior", + "unless --local is provided.") {|remote| @remote = remote} + end + end + + def parse!(args) + options.order!(args) + unless @sources.empty? + @sources.map!{ |uri| Repository.new(uri) } + else + @sources = Repositories.instance.all + end + if @remote + @sources.map{|r| r.plugins}.flatten.each do |plugin| + if @local or !plugin.installed? + puts plugin.to_s + end + end + else + cd "#{@base_command.environment.root}/vendor/plugins" + Dir["*"].select{|p| File.directory?(p)}.each do |name| + puts name + end + end + end + end + + + class Sources + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} sources [OPTIONS] [PATTERN]" + o.define_head "List configured plugin repositories." + o.separator "" + o.separator "Options:" + o.separator "" + o.on( "-c", "--check", + "Report status of repository.") { |sources| @sources = sources} + end + end + + def parse!(args) + options.parse!(args) + Repositories.each do |repo| + puts repo.uri + end + end + end + + + class Source + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} source REPOSITORY [REPOSITORY [REPOSITORY]...]" + o.define_head "Add new repositories to the default search list." + end + end + + def parse!(args) + options.parse!(args) + count = 0 + args.each do |uri| + if Repositories.instance.add(uri) + puts "added: #{uri.ljust(50)}" if $verbose + count += 1 + else + puts "failed: #{uri.ljust(50)}" + end + end + Repositories.instance.save + puts "Added #{count} repositories." + end + end + + + class Unsource + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} unsource URI [URI [URI]...]" + o.define_head "Remove repositories from the default search list." + o.separator "" + o.on_tail("-h", "--help", "Show this help message.") { puts o; exit } + end + end + + def parse!(args) + options.parse!(args) + count = 0 + args.each do |uri| + if Repositories.instance.remove(uri) + count += 1 + puts "removed: #{uri.ljust(50)}" + else + puts "failed: #{uri.ljust(50)}" + end + end + Repositories.instance.save + puts "Removed #{count} repositories." + end + end + + + class Discover + def initialize(base_command) + @base_command = base_command + @list = false + @prompt = true + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} discover URI [URI [URI]...]" + o.define_head "Discover repositories referenced on a page." + o.separator "" + o.separator "Options:" + o.separator "" + o.on( "-l", "--list", + "List but don't prompt or add discovered repositories.") { |list| @list, @prompt = list, !@list } + o.on( "-n", "--no-prompt", + "Add all new repositories without prompting.") { |v| @prompt = !v } + end + end + + def parse!(args) + options.parse!(args) + args = ['http://wiki.rubyonrails.org/rails/pages/Plugins'] if args.empty? + args.each do |uri| + scrape(uri) do |repo_uri| + catch(:next_uri) do + if @prompt + begin + $stdout.print "Add #{repo_uri}? [Y/n] " + throw :next_uri if $stdin.gets !~ /^y?$/i + rescue Interrupt + $stdout.puts + exit 1 + end + elsif @list + puts repo_uri + throw :next_uri + end + Repositories.instance.add(repo_uri) + puts "discovered: #{repo_uri}" if $verbose or !@prompt + end + end + end + Repositories.instance.save + end + + def scrape(uri) + require 'open-uri' + puts "Scraping #{uri}" if $verbose + dupes = [] + content = open(uri).each do |line| + begin + if line =~ /]*href=['"]([^'"]*)['"]/ || line =~ /(svn:\/\/[^<|\n]*)/ + uri = $1 + if uri =~ /^\w+:\/\// && uri =~ /\/plugins\// && uri !~ /\/browser\// && uri !~ /^http:\/\/wiki\.rubyonrails/ && uri !~ /http:\/\/instiki/ + uri = extract_repository_uri(uri) + yield uri unless dupes.include?(uri) || Repositories.instance.exist?(uri) + dupes << uri + end + end + rescue + puts "Problems scraping '#{uri}': #{$!.to_s}" + end + end + end + + def extract_repository_uri(uri) + uri.match(/(svn|https?):.*\/plugins\//i)[0] + end + end + + class Install + def initialize(base_command) + @base_command = base_command + @method = :http + @options = { :quiet => false, :revision => nil, :force => false } + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]" + o.define_head "Install one or more plugins." + o.separator "" + o.separator "Options:" + o.on( "-x", "--externals", + "Use svn:externals to grab the plugin.", + "Enables plugin updates and plugin versioning.") { |v| @method = :externals } + o.on( "-o", "--checkout", + "Use svn checkout to grab the plugin.", + "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout } + o.on( "-e", "--export", + "Use svn export to grab the plugin.", + "Exports the plugin, allowing you to check it into your local repository. Does not enable updates, or add an svn:externals entry.") { |v| @method = :export } + o.on( "-q", "--quiet", + "Suppresses the output from installation.", + "Ignored if -v is passed (./script/plugin -v install ...)") { |v| @options[:quiet] = true } + o.on( "-r REVISION", "--revision REVISION", + "Checks out the given revision from subversion or git.", + "Ignored if subversion/git is not used.") { |v| @options[:revision] = v } + o.on( "-f", "--force", + "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true } + o.separator "" + o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to " + o.separator "a plugin repository." + end + end + + def determine_install_method + best = @base_command.environment.best_install_method + @method = :http if best == :http and @method == :export + case + when (best == :http and @method != :http) + msg = "Cannot install using subversion because `svn' cannot be found in your PATH" + when (best == :export and (@method != :export and @method != :http)) + msg = "Cannot install using #{@method} because this project is not under subversion." + when (best != :externals and @method == :externals) + msg = "Cannot install using externals because vendor/plugins is not under subversion." + end + if msg + puts msg + exit 1 + end + @method + end + + def parse!(args) + options.parse!(args) + environment = @base_command.environment + install_method = determine_install_method + puts "Plugins will be installed using #{install_method}" if $verbose + args.each do |name| + ::Plugin.find(name).install(install_method, @options) + end + rescue StandardError => e + puts "Plugin not found: #{args.inspect}" + puts e.inspect if $verbose + exit 1 + end + end + + class Update + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} update [name [name]...]" + o.on( "-r REVISION", "--revision REVISION", + "Checks out the given revision from subversion.", + "Ignored if subversion is not used.") { |v| @revision = v } + o.define_head "Update plugins." + end + end + + def parse!(args) + options.parse!(args) + root = @base_command.environment.root + cd root + args = Dir["vendor/plugins/*"].map do |f| + File.directory?("#{f}/.svn") ? File.basename(f) : nil + end.compact if args.empty? + cd "vendor/plugins" + args.each do |name| + if File.directory?(name) + puts "Updating plugin: #{name}" + system("svn #{$verbose ? '' : '-q'} up \"#{name}\" #{@revision ? "-r #{@revision}" : ''}") + else + puts "Plugin doesn't exist: #{name}" + end + end + end + end + + class Remove + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} remove name [name]..." + o.define_head "Remove plugins." + end + end + + def parse!(args) + options.parse!(args) + root = @base_command.environment.root + args.each do |name| + ::Plugin.new(name).uninstall + end + end + end + + class Info + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} info name [name]..." + o.define_head "Shows plugin info at {url}/about.yml." + end + end + + def parse!(args) + options.parse!(args) + args.each do |name| + puts ::Plugin.find(name).info + puts + end + end + end +end + +class RecursiveHTTPFetcher + attr_accessor :quiet + def initialize(urls_to_fetch, level = 1, cwd = ".") + @level = level + @cwd = cwd + @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a + @quiet = false + end + + def ls + @urls_to_fetch.collect do |url| + if url =~ /^svn(\+ssh)?:\/\/.*/ + `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil + else + open(url) do |stream| + links("", stream.read) + end rescue nil + end + end.flatten + end + + def push_d(dir) + @cwd = File.join(@cwd, dir) + FileUtils.mkdir_p(@cwd) + end + + def pop_d + @cwd = File.dirname(@cwd) + end + + def links(base_url, contents) + links = [] + contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link| + link = link.sub(/href="/i, "") + next if link =~ /svnindex.xsl$/ + next if link =~ /^(\w*:|)\/\// || link =~ /^\./ + links << File.join(base_url, link) + end + links + end + + def download(link) + puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet + open(link) do |stream| + File.open(File.join(@cwd, File.basename(link)), "wb") do |file| + file.write(stream.read) + end + end + end + + def fetch(links = @urls_to_fetch) + links.each do |l| + (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l) + end + end + + def fetch_dir(url) + @level += 1 + push_d(File.basename(url)) if @level > 0 + open(url) do |stream| + contents = stream.read + fetch(links(url, contents)) + end + pop_d if @level > 0 + @level -= 1 + end +end + +Commands::Plugin.parse! diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/runner.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/runner.rb new file mode 100644 index 00000000000..510128318a5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/runner.rb @@ -0,0 +1,54 @@ +require 'optparse' + +options = { :environment => (ENV['RAILS_ENV'] || "development").dup } +code_or_file = nil + +ARGV.clone.options do |opts| + script_name = File.basename($0) + opts.banner = "Usage: #{$0} [options] ('Some.ruby(code)' or a filename)" + + opts.separator "" + + opts.on("-e", "--environment=name", String, + "Specifies the environment for the runner to operate under (test/development/production).", + "Default: development") { |v| options[:environment] = v } + + opts.separator "" + + opts.on("-h", "--help", + "Show this help message.") { $stderr.puts opts; exit } + + if RUBY_PLATFORM !~ /mswin/ + opts.separator "" + opts.separator "You can also use runner as a shebang line for your scripts like this:" + opts.separator "-------------------------------------------------------------" + opts.separator "#!/usr/bin/env #{File.expand_path($0)}" + opts.separator "" + opts.separator "Product.find(:all).each { |p| p.price *= 2 ; p.save! }" + opts.separator "-------------------------------------------------------------" + end + + opts.order! { |o| code_or_file ||= o } rescue retry +end + +ARGV.delete(code_or_file) + +ENV["RAILS_ENV"] = options[:environment] +RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) + +require RAILS_ROOT + '/config/environment' + +begin + if code_or_file.nil? + $stderr.puts "Run '#{$0} -h' for help." + exit 1 + elsif File.exist?(code_or_file) + eval(File.read(code_or_file), nil, code_or_file) + else + eval(code_or_file) + end +ensure + if defined? Rails + Rails.logger.flush if Rails.logger.respond_to?(:flush) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/server.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/server.rb new file mode 100644 index 00000000000..b35c6871b1e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/server.rb @@ -0,0 +1,114 @@ +require 'active_support' +require 'action_controller' + +require 'fileutils' +require 'optparse' + +# TODO: Push Thin adapter upstream so we don't need worry about requiring it +begin + require_library_or_gem 'thin' +rescue Exception + # Thin not available +end + +options = { + :Port => 3000, + :Host => "127.0.0.1", + :environment => (ENV['RAILS_ENV'] || "development").dup, + :config => RAILS_ROOT + "/config.ru", + :detach => false, + :debugger => false, + :path => nil +} + +ARGV.clone.options do |opts| + opts.on("-p", "--port=port", Integer, + "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } + opts.on("-b", "--binding=ip", String, + "Binds Rails to the specified ip.", "Default: 0.0.0.0") { |v| options[:Host] = v } + opts.on("-c", "--config=file", String, + "Use custom rackup configuration file") { |v| options[:config] = v } + opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:detach] = true } + opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { options[:debugger] = true } + opts.on("-e", "--environment=name", String, + "Specifies the environment to run this server under (test/development/production).", + "Default: development") { |v| options[:environment] = v } + opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: /") { |v| options[:path] = v } + + opts.separator "" + + opts.on("-h", "--help", "Show this help message.") { puts opts; exit } + + opts.parse! +end + +server = Rack::Handler.get(ARGV.first) rescue nil +unless server + begin + server = Rack::Handler::Mongrel + rescue LoadError => e + server = Rack::Handler::WEBrick + end +end + +puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" +puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}#{options[:path]}" + +%w(cache pids sessions sockets).each do |dir_to_make| + FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) +end + +if options[:detach] + Process.daemon + pid = "#{RAILS_ROOT}/tmp/pids/server.pid" + File.open(pid, 'w'){ |f| f.write(Process.pid) } + at_exit { File.delete(pid) if File.exist?(pid) } +end + +ENV["RAILS_ENV"] = options[:environment] +RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) + +if File.exist?(options[:config]) + config = options[:config] + if config =~ /\.ru$/ + cfgfile = File.read(config) + if cfgfile[/^#\\(.*)/] + opts.parse!($1.split(/\s+/)) + end + inner_app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config) + else + require config + inner_app = Object.const_get(File.basename(config, '.rb').capitalize) + end +else + require RAILS_ROOT + "/config/environment" + inner_app = ActionController::Dispatcher.new +end + +if options[:path].nil? + map_path = "/" +else + ActionController::Base.relative_url_root = options[:path] + map_path = options[:path] +end + +app = Rack::Builder.new { + use Rails::Rack::LogTailer unless options[:detach] + use Rails::Rack::Debugger if options[:debugger] + map map_path do + use Rails::Rack::Static + run inner_app + end +}.to_app + +puts "=> Call with -d to detach" + +trap(:INT) { exit } + +puts "=> Ctrl-C to shutdown server" + +begin + server.run(app, options.merge(:AccessLog => [])) +ensure + puts 'Exiting' +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/update.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/update.rb new file mode 100644 index 00000000000..83ef833300a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/commands/update.rb @@ -0,0 +1,4 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails_generator' +require 'rails_generator/scripts/update' +Rails::Generator::Scripts::Update.new.run(ARGV) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_app.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_app.rb new file mode 100644 index 00000000000..d7d01d703fc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_app.rb @@ -0,0 +1,30 @@ +require 'active_support/test_case' +require 'action_controller' + +# work around the at_exit hook in test/unit, which kills IRB +Test::Unit.run = true if Test::Unit.respond_to?(:run=) + +# reference the global "app" instance, created on demand. To recreate the +# instance, pass a non-false value as the parameter. +def app(create=false) + @app_integration_instance = nil if create + @app_integration_instance ||= new_session do |sess| + sess.host! "www.example.com" + end +end + +# create a new session. If a block is given, the new session will be yielded +# to the block before being returned. +def new_session + session = ActionController::Integration::Session.new + yield session if block_given? + session +end + +#reloads the environment +def reload! + puts "Reloading..." + Dispatcher.cleanup_application + Dispatcher.reload_application + true +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_sandbox.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_sandbox.rb new file mode 100644 index 00000000000..65a3d686191 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_sandbox.rb @@ -0,0 +1,6 @@ +ActiveRecord::Base.connection.increment_open_transactions +ActiveRecord::Base.connection.begin_db_transaction +at_exit do + ActiveRecord::Base.connection.rollback_db_transaction + ActiveRecord::Base.connection.decrement_open_transactions +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_with_helpers.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_with_helpers.rb new file mode 100644 index 00000000000..039db667c40 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/console_with_helpers.rb @@ -0,0 +1,5 @@ +def helper + @helper ||= ApplicationController.helpers +end + +@controller = ApplicationController.new diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/dispatcher.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/dispatcher.rb new file mode 100644 index 00000000000..9f8b59aa3d2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/dispatcher.rb @@ -0,0 +1,24 @@ +#-- +# Copyright (c) 2004-2009 David Heinemeier Hansson +# +# 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. +#++ +require 'action_controller/dispatcher' +Dispatcher = ActionController::Dispatcher diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/fcgi_handler.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/fcgi_handler.rb new file mode 100644 index 00000000000..0cd2dc51c60 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/fcgi_handler.rb @@ -0,0 +1,239 @@ +require 'fcgi' +require 'logger' +require 'dispatcher' +require 'rbconfig' + +class RailsFCGIHandler + SIGNALS = { + 'HUP' => :reload, + 'INT' => :exit_now, + 'TERM' => :exit_now, + 'USR1' => :exit, + 'USR2' => :restart + } + GLOBAL_SIGNALS = SIGNALS.keys - %w(USR1) + + attr_reader :when_ready + + attr_accessor :log_file_path + attr_accessor :gc_request_period + + # Initialize and run the FastCGI instance, passing arguments through to new. + def self.process!(*args, &block) + new(*args, &block).process! + end + + # Initialize the FastCGI instance with the path to a crash log + # detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log) + # and the number of requests to process between garbage collection runs + # (default nil for normal GC behavior.) Optionally, pass a block which + # takes this instance as an argument for further configuration. + def initialize(log_file_path = nil, gc_request_period = nil) + self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log" + self.gc_request_period = gc_request_period + + # Yield for additional configuration. + yield self if block_given? + + # Safely install signal handlers. + install_signal_handlers + + @app = Dispatcher.new + + # Start error timestamp at 11 seconds ago. + @last_error_on = Time.now - 11 + end + + def process!(provider = FCGI) + mark_features! + + dispatcher_log :info, 'starting' + process_each_request provider + dispatcher_log :info, 'stopping gracefully' + + rescue Exception => error + case error + when SystemExit + dispatcher_log :info, 'stopping after explicit exit' + when SignalException + dispatcher_error error, 'stopping after unhandled signal' + else + # Retry if exceptions occur more than 10 seconds apart. + if Time.now - @last_error_on > 10 + @last_error_on = Time.now + dispatcher_error error, 'retrying after unhandled exception' + retry + else + dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last' + end + end + end + + protected + def process_each_request(provider) + request = nil + + catch :exit do + provider.each do |request| + process_request(request) + + case when_ready + when :reload + reload! + when :restart + close_connection(request) + restart! + when :exit + close_connection(request) + throw :exit + end + end + end + rescue SignalException => signal + raise unless signal.message == 'SIGUSR1' + close_connection(request) + end + + def process_request(request) + @processing, @when_ready = true, nil + gc_countdown + + with_signal_handler 'USR1' do + begin + ::Rack::Handler::FastCGI.serve(request, @app) + rescue SignalException, SystemExit + raise + rescue Exception => error + dispatcher_error error, 'unhandled dispatch error' + end + end + ensure + @processing = false + end + + def logger + @logger ||= Logger.new(@log_file_path) + end + + def dispatcher_log(level, msg) + time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S") + logger.send(level, "[#{time_str} :: #{$$}] #{msg}") + rescue Exception => log_error # Logger errors + STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n" + STDERR << " #{log_error.class}: #{log_error.message}\n" + end + + def dispatcher_error(e, msg = "") + error_message = + "Dispatcher failed to catch: #{e} (#{e.class})\n" + + " #{e.backtrace.join("\n ")}\n#{msg}" + dispatcher_log(:error, error_message) + end + + def install_signal_handlers + GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) } + end + + def install_signal_handler(signal, handler = nil) + if SIGNALS.include?(signal) && self.class.method_defined?(name = "#{SIGNALS[signal]}_handler") + handler ||= method(name).to_proc + + begin + trap(signal, handler) + rescue ArgumentError + dispatcher_log :warn, "Ignoring unsupported signal #{signal}." + end + else + dispatcher_log :warn, "Ignoring unsupported signal #{signal}." + end + end + + def with_signal_handler(signal) + install_signal_handler(signal) + yield + ensure + install_signal_handler(signal, 'DEFAULT') + end + + def exit_now_handler(signal) + dispatcher_log :info, "asked to stop immediately" + exit + end + + def exit_handler(signal) + dispatcher_log :info, "asked to stop ASAP" + if @processing + @when_ready = :exit + else + throw :exit + end + end + + def reload_handler(signal) + dispatcher_log :info, "asked to reload ASAP" + if @processing + @when_ready = :reload + else + reload! + end + end + + def restart_handler(signal) + dispatcher_log :info, "asked to restart ASAP" + if @processing + @when_ready = :restart + else + restart! + end + end + + def restart! + config = ::Config::CONFIG + ruby = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT'] + command_line = [ruby, $0, ARGV].flatten.join(' ') + + dispatcher_log :info, "restarted" + + # close resources as they won't be closed by + # the OS when using exec + logger.close rescue nil + Rails.logger.close rescue nil + + exec(command_line) + end + + def reload! + run_gc! if gc_request_period + restore! + @when_ready = nil + dispatcher_log :info, "reloaded" + end + + # Make a note of $" so we can safely reload this instance. + def mark_features! + @features = $".clone + end + + def restore! + $".replace @features + Dispatcher.reset_application! + ActionController::Routing::Routes.reload + end + + def run_gc! + @gc_request_countdown = gc_request_period + GC.enable; GC.start; GC.disable + end + + def gc_countdown + if gc_request_period + @gc_request_countdown ||= gc_request_period + @gc_request_countdown -= 1 + run_gc! if @gc_request_countdown <= 0 + end + end + + def close_connection(request) + request.finish if request + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/initializer.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/initializer.rb new file mode 100644 index 00000000000..196bd93b0c7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/initializer.rb @@ -0,0 +1,1128 @@ +require 'logger' +require 'set' +require 'pathname' + +$LOAD_PATH.unshift File.dirname(__FILE__) +require 'railties_path' +require 'rails/version' +require 'rails/plugin/locator' +require 'rails/plugin/loader' +require 'rails/gem_dependency' +require 'rails/rack' + + +RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV) + +module Rails + class << self + # The Configuration instance used to configure the Rails environment + def configuration + @@configuration + end + + def configuration=(configuration) + @@configuration = configuration + end + + def initialized? + @initialized || false + end + + def initialized=(initialized) + @initialized ||= initialized + end + + def logger + if defined?(RAILS_DEFAULT_LOGGER) + RAILS_DEFAULT_LOGGER + else + nil + end + end + + def backtrace_cleaner + @@backtrace_cleaner ||= begin + # Relies on ActiveSupport, so we have to lazy load to postpone definition until AS has been loaded + require 'rails/backtrace_cleaner' + Rails::BacktraceCleaner.new + end + end + + def root + Pathname.new(RAILS_ROOT) if defined?(RAILS_ROOT) + end + + def env + @_env ||= ActiveSupport::StringInquirer.new(RAILS_ENV) + end + + def cache + RAILS_CACHE + end + + def version + VERSION::STRING + end + + def public_path + @@public_path ||= self.root ? File.join(self.root, "public") : "public" + end + + def public_path=(path) + @@public_path = path + end + end + + # The Initializer is responsible for processing the Rails configuration, such + # as setting the $LOAD_PATH, requiring the right frameworks, initializing + # logging, and more. It can be run either as a single command that'll just + # use the default configuration, like this: + # + # Rails::Initializer.run + # + # But normally it's more interesting to pass in a custom configuration + # through the block running: + # + # Rails::Initializer.run do |config| + # config.frameworks -= [ :action_mailer ] + # end + # + # This will use the default configuration options from Rails::Configuration, + # but allow for overwriting on select areas. + class Initializer + # The Configuration instance used by this Initializer instance. + attr_reader :configuration + + # The set of loaded plugins. + attr_reader :loaded_plugins + + # Whether or not all the gem dependencies have been met + attr_reader :gems_dependencies_loaded + + # Runs the initializer. By default, this will invoke the #process method, + # which simply executes all of the initialization routines. Alternately, + # you can specify explicitly which initialization routine you want: + # + # Rails::Initializer.run(:set_load_path) + # + # This is useful if you only want the load path initialized, without + # incurring the overhead of completely loading the entire environment. + def self.run(command = :process, configuration = Configuration.new) + yield configuration if block_given? + initializer = new configuration + initializer.send(command) + initializer + end + + # Create a new Initializer instance that references the given Configuration + # instance. + def initialize(configuration) + @configuration = configuration + @loaded_plugins = [] + end + + # Sequentially step through all of the available initialization routines, + # in order (view execution order in source). + def process + Rails.configuration = configuration + + check_ruby_version + install_gem_spec_stubs + set_load_path + add_gem_load_paths + + require_frameworks + set_autoload_paths + add_plugin_load_paths + load_environment + preload_frameworks + + initialize_encoding + initialize_database + + initialize_cache + initialize_framework_caches + + initialize_logger + initialize_framework_logging + + initialize_dependency_mechanism + initialize_whiny_nils + + initialize_time_zone + initialize_i18n + + initialize_framework_settings + initialize_framework_views + + initialize_metal + + add_support_load_paths + + check_for_unbuilt_gems + + load_gems + load_plugins + + # pick up any gems that plugins depend on + add_gem_load_paths + load_gems + check_gem_dependencies + + # bail out if gems are missing - note that check_gem_dependencies will have + # already called abort() unless $gems_rake_task is set + return unless gems_dependencies_loaded + + load_application_initializers + + # the framework is now fully initialized + after_initialize + + # Setup database middleware after initializers have run + initialize_database_middleware + + # Prepare dispatcher callbacks and run 'prepare' callbacks + prepare_dispatcher + + # Routing must be initialized after plugins to allow the former to extend the routes + initialize_routing + + # Observers are loaded after plugins in case Observers or observed models are modified by plugins. + load_observers + + # Load view path cache + load_view_paths + + # Load application classes + load_application_classes + + # Disable dependency loading during request cycle + disable_dependency_loading + + # Flag initialized + Rails.initialized = true + end + + # Check for valid Ruby version + # This is done in an external file, so we can use it + # from the `rails` program as well without duplication. + def check_ruby_version + require 'ruby_version_check' + end + + # If Rails is vendored and RubyGems is available, install stub GemSpecs + # for Rails, Active Support, Active Record, Action Pack, Action Mailer, and + # Active Resource. This allows Gem plugins to depend on Rails even when + # the Gem version of Rails shouldn't be loaded. + def install_gem_spec_stubs + unless Rails.respond_to?(:vendor_rails?) + abort %{Your config/boot.rb is outdated: Run "rake rails:update".} + end + + if Rails.vendor_rails? + begin; require "rubygems"; rescue LoadError; return; end + + stubs = %w(rails activesupport activerecord actionpack actionmailer activeresource) + stubs.reject! { |s| Gem.loaded_specs.key?(s) } + + stubs.each do |stub| + Gem.loaded_specs[stub] = Gem::Specification.new do |s| + s.name = stub + s.version = Rails::VERSION::STRING + s.loaded_from = "" + end + end + end + end + + # Set the $LOAD_PATH based on the value of + # Configuration#load_paths. Duplicates are removed. + def set_load_path + load_paths = configuration.load_paths + configuration.framework_paths + load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) } + $LOAD_PATH.uniq! + end + + # Set the paths from which Rails will automatically load source files, and + # the load_once paths. + def set_autoload_paths + ActiveSupport::Dependencies.load_paths = configuration.load_paths.uniq + ActiveSupport::Dependencies.load_once_paths = configuration.load_once_paths.uniq + + extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths + unless extra.empty? + abort <<-end_error + load_once_paths must be a subset of the load_paths. + Extra items in load_once_paths: #{extra * ','} + end_error + end + + # Freeze the arrays so future modifications will fail rather than do nothing mysteriously + configuration.load_once_paths.freeze + end + + # Requires all frameworks specified by the Configuration#frameworks + # list. By default, all frameworks (Active Record, Active Support, + # Action Pack, Action Mailer, and Active Resource) are loaded. + def require_frameworks + configuration.frameworks.each { |framework| require(framework.to_s) } + rescue LoadError => e + # Re-raise as RuntimeError because Mongrel would swallow LoadError. + raise e.to_s + end + + # Preload all frameworks specified by the Configuration#frameworks. + # Used by Passenger to ensure everything's loaded before forking and + # to avoid autoload race conditions in JRuby. + def preload_frameworks + if configuration.preload_frameworks + configuration.frameworks.each do |framework| + # String#classify and #constantize aren't available yet. + toplevel = Object.const_get(framework.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }) + toplevel.load_all! if toplevel.respond_to?(:load_all!) + end + end + end + + # Add the load paths used by support functions such as the info controller + def add_support_load_paths + end + + # Adds all load paths from plugins to the global set of load paths, so that + # code from plugins can be required (explicitly or automatically via ActiveSupport::Dependencies). + def add_plugin_load_paths + plugin_loader.add_plugin_load_paths + end + + def add_gem_load_paths + Rails::GemDependency.add_frozen_gem_path + unless @configuration.gems.empty? + require "rubygems" + @configuration.gems.each { |gem| gem.add_load_paths } + end + end + + def load_gems + unless $gems_rake_task + @configuration.gems.each { |gem| gem.load } + end + end + + def check_for_unbuilt_gems + unbuilt_gems = @configuration.gems.select(&:frozen?).reject(&:built?) + if unbuilt_gems.size > 0 + # don't print if the gems:build rake tasks are being run + unless $gems_build_rake_task + abort <<-end_error +The following gems have native components that need to be built + #{unbuilt_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "} + +You're running: + ruby #{Gem.ruby_version} at #{Gem.ruby} + rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '} + +Run `rake gems:build` to build the unbuilt gems. + end_error + end + end + end + + def check_gem_dependencies + unloaded_gems = @configuration.gems.reject { |g| g.loaded? } + if unloaded_gems.size > 0 + @gems_dependencies_loaded = false + # don't print if the gems rake tasks are being run + unless $gems_rake_task + abort <<-end_error +Missing these required gems: + #{unloaded_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "} + +You're running: + ruby #{Gem.ruby_version} at #{Gem.ruby} + rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '} + +Run `rake gems:install` to install the missing gems. + end_error + end + else + @gems_dependencies_loaded = true + end + end + + # Loads all plugins in config.plugin_paths. plugin_paths + # defaults to vendor/plugins but may also be set to a list of + # paths, such as + # config.plugin_paths = ["#{RAILS_ROOT}/lib/plugins", "#{RAILS_ROOT}/vendor/plugins"] + # + # In the default implementation, as each plugin discovered in plugin_paths is initialized: + # * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory) + # * init.rb is evaluated, if present + # + # After all plugins are loaded, duplicates are removed from the load path. + # If an array of plugin names is specified in config.plugins, only those plugins will be loaded + # and they plugins will be loaded in that order. Otherwise, plugins are loaded in alphabetical + # order. + # + # if config.plugins ends contains :all then the named plugins will be loaded in the given order and all other + # plugins will be loaded in alphabetical order + def load_plugins + plugin_loader.load_plugins + end + + def plugin_loader + @plugin_loader ||= configuration.plugin_loader.new(self) + end + + # Loads the environment specified by Configuration#environment_path, which + # is typically one of development, test, or production. + def load_environment + silence_warnings do + return if @environment_loaded + @environment_loaded = true + + config = configuration + constants = self.class.constants + + eval(IO.read(configuration.environment_path), binding, configuration.environment_path) + + (self.class.constants - constants).each do |const| + Object.const_set(const, self.class.const_get(const)) + end + end + end + + def load_observers + if gems_dependencies_loaded && configuration.frameworks.include?(:active_record) + ActiveRecord::Base.instantiate_observers + end + end + + def load_view_paths + if configuration.frameworks.include?(:action_view) + ActionController::Base.view_paths.load! if configuration.frameworks.include?(:action_controller) + ActionMailer::Base.view_paths.load! if configuration.frameworks.include?(:action_mailer) + end + end + + # Eager load application classes + def load_application_classes + return if $rails_rake_task + if configuration.cache_classes + configuration.eager_load_paths.each do |load_path| + matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ + Dir.glob("#{load_path}/**/*.rb").sort.each do |file| + require_dependency file.sub(matcher, '\1') + end + end + end + end + + # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the + # multibyte safe operations. Plugin authors supporting other encodings + # should override this behaviour and set the relevant +default_charset+ + # on ActionController::Base. + # + # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby + # shebang line if you don't want UTF-8. + def initialize_encoding + $KCODE='u' if RUBY_VERSION < '1.9' + end + + # This initialization routine does nothing unless :active_record + # is one of the frameworks to load (Configuration#frameworks). If it is, + # this sets the database configuration from Configuration#database_configuration + # and then establishes the connection. + def initialize_database + if configuration.frameworks.include?(:active_record) + ActiveRecord::Base.configurations = configuration.database_configuration + ActiveRecord::Base.establish_connection + end + end + + def initialize_database_middleware + if configuration.frameworks.include?(:active_record) + if configuration.frameworks.include?(:action_controller) && + ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' + configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement + configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache + else + configuration.middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement + configuration.middleware.use ActiveRecord::QueryCache + end + end + end + + def initialize_cache + unless defined?(RAILS_CACHE) + silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(configuration.cache_store) } + + if RAILS_CACHE.respond_to?(:middleware) + # Insert middleware to setup and teardown local cache for each request + configuration.middleware.insert_after(:"ActionController::Failsafe", RAILS_CACHE.middleware) + end + end + end + + def initialize_framework_caches + if configuration.frameworks.include?(:action_controller) + ActionController::Base.cache_store ||= RAILS_CACHE + end + end + + # If the RAILS_DEFAULT_LOGGER constant is already set, this initialization + # routine does nothing. If the constant is not set, and Configuration#logger + # is not +nil+, this also does nothing. Otherwise, a new logger instance + # is created at Configuration#log_path, with a default log level of + # Configuration#log_level. + # + # If the log could not be created, the log will be set to output to + # +STDERR+, with a log level of +WARN+. + def initialize_logger + # if the environment has explicitly defined a logger, use it + return if Rails.logger + + unless logger = configuration.logger + begin + logger = ActiveSupport::BufferedLogger.new(configuration.log_path) + logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase) + if configuration.environment == "production" + logger.auto_flushing = false + end + rescue StandardError => e + logger = ActiveSupport::BufferedLogger.new(STDERR) + logger.level = ActiveSupport::BufferedLogger::WARN + logger.warn( + "Rails Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " + + "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." + ) + end + end + + silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger } + end + + # Sets the logger for Active Record, Action Controller, and Action Mailer + # (but only for those frameworks that are to be loaded). If the framework's + # logger is already set, it is not changed, otherwise it is set to use + # RAILS_DEFAULT_LOGGER. + def initialize_framework_logging + for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks) + framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger + end + + ActiveSupport::Dependencies.logger ||= Rails.logger + Rails.cache.logger ||= Rails.logger + end + + # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ + # (but only for those frameworks that are to be loaded). If the framework's + # paths have already been set, it is not changed, otherwise it is + # set to use Configuration#view_path. + def initialize_framework_views + if configuration.frameworks.include?(:action_view) + view_path = ActionView::PathSet.type_cast(configuration.view_path) + ActionMailer::Base.template_root = view_path if configuration.frameworks.include?(:action_mailer) && ActionMailer::Base.view_paths.blank? + ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.blank? + end + end + + # If Action Controller is not one of the loaded frameworks (Configuration#frameworks) + # this does nothing. Otherwise, it loads the routing definitions and sets up + # loading module used to lazily load controllers (Configuration#controller_paths). + def initialize_routing + return unless configuration.frameworks.include?(:action_controller) + + ActionController::Routing.controller_paths += configuration.controller_paths + ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file) + ActionController::Routing::Routes.reload! + end + + # Sets the dependency loading mechanism based on the value of + # Configuration#cache_classes. + def initialize_dependency_mechanism + ActiveSupport::Dependencies.mechanism = configuration.cache_classes ? :require : :load + end + + # Loads support for "whiny nil" (noisy warnings when methods are invoked + # on +nil+ values) if Configuration#whiny_nils is true. + def initialize_whiny_nils + require('active_support/whiny_nil') if configuration.whiny_nils + end + + # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes. + # If assigned value cannot be matched to a TimeZone, an exception will be raised. + def initialize_time_zone + if configuration.time_zone + zone_default = Time.__send__(:get_zone, configuration.time_zone) + + unless zone_default + raise \ + 'Value assigned to config.time_zone not recognized.' + + 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' + end + + Time.zone_default = zone_default + + if configuration.frameworks.include?(:active_record) + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + end + end + end + + # Set the i18n configuration from config.i18n but special-case for the load_path which should be + # appended to what's already set instead of overwritten. + def initialize_i18n + configuration.i18n.each do |setting, value| + if setting == :load_path + I18n.load_path += value + else + I18n.send("#{setting}=", value) + end + end + end + + def initialize_metal + Rails::Rack::Metal.requested_metals = configuration.metals + Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths + + configuration.middleware.insert_before( + :"ActionController::ParamsParser", + Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?) + end + + # Initializes framework-specific settings for each of the loaded frameworks + # (Configuration#frameworks). The available settings map to the accessors + # on each of the corresponding Base classes. + def initialize_framework_settings + configuration.frameworks.each do |framework| + base_class = framework.to_s.camelize.constantize.const_get("Base") + + configuration.send(framework).each do |setting, value| + base_class.send("#{setting}=", value) + end + end + configuration.active_support.each do |setting, value| + ActiveSupport.send("#{setting}=", value) + end + end + + # Fires the user-supplied after_initialize block (Configuration#after_initialize) + def after_initialize + if gems_dependencies_loaded + configuration.after_initialize_blocks.each do |block| + block.call + end + end + end + + def load_application_initializers + if gems_dependencies_loaded + Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer| + load(initializer) + end + end + end + + def prepare_dispatcher + return unless configuration.frameworks.include?(:action_controller) + require 'dispatcher' unless defined?(::Dispatcher) + Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) + Dispatcher.run_prepare_callbacks + end + + def disable_dependency_loading + if configuration.cache_classes && !configuration.dependency_loading + ActiveSupport::Dependencies.unhook! + end + end + end + + # The Configuration class holds all the parameters for the Initializer and + # ships with defaults that suites most Rails applications. But it's possible + # to overwrite everything. Usually, you'll create an Configuration file + # implicitly through the block running on the Initializer, but it's also + # possible to create the Configuration instance in advance and pass it in + # like this: + # + # config = Rails::Configuration.new + # Rails::Initializer.run(:process, config) + class Configuration + # The application's base directory. + attr_reader :root_path + + # A stub for setting options on ActionController::Base. + attr_accessor :action_controller + + # A stub for setting options on ActionMailer::Base. + attr_accessor :action_mailer + + # A stub for setting options on ActionView::Base. + attr_accessor :action_view + + # A stub for setting options on ActiveRecord::Base. + attr_accessor :active_record + + # A stub for setting options on ActiveResource::Base. + attr_accessor :active_resource + + # A stub for setting options on ActiveSupport. + attr_accessor :active_support + + # Whether to preload all frameworks at startup. + attr_accessor :preload_frameworks + + # Whether or not classes should be cached (set to false if you want + # application classes to be reloaded on each request) + attr_accessor :cache_classes + + # The list of paths that should be searched for controllers. (Defaults + # to app/controllers.) + attr_accessor :controller_paths + + # The path to the database configuration file to use. (Defaults to + # config/database.yml.) + attr_accessor :database_configuration_file + + # The path to the routes configuration file to use. (Defaults to + # config/routes.rb.) + attr_accessor :routes_configuration_file + + # The list of rails framework components that should be loaded. (Defaults + # to :active_record, :action_controller, + # :action_view, :action_mailer, and + # :active_resource). + attr_accessor :frameworks + + # An array of additional paths to prepend to the load path. By default, + # all +app+, +lib+, +vendor+ and mock paths are included in this list. + attr_accessor :load_paths + + # An array of paths from which Rails will automatically load from only once. + # All elements of this array must also be in +load_paths+. + attr_accessor :load_once_paths + + # An array of paths from which Rails will eager load on boot if cache + # classes is enabled. All elements of this array must also be in + # +load_paths+. + attr_accessor :eager_load_paths + + # The log level to use for the default Rails logger. In production mode, + # this defaults to :info. In development mode, it defaults to + # :debug. + attr_accessor :log_level + + # The path to the log file to use. Defaults to log/#{environment}.log + # (e.g. log/development.log or log/production.log). + attr_accessor :log_path + + # The specific logger to use. By default, a logger will be created and + # initialized using #log_path and #log_level, but a programmer may + # specifically set the logger to use via this accessor and it will be + # used directly. + attr_accessor :logger + + # The specific cache store to use. By default, the ActiveSupport::Cache::Store will be used. + attr_accessor :cache_store + + # The root of the application's views. (Defaults to app/views.) + attr_accessor :view_path + + # Set to +true+ if you want to be warned (noisily) when you try to invoke + # any method of +nil+. Set to +false+ for the standard Ruby behavior. + attr_accessor :whiny_nils + + # The list of plugins to load. If this is set to nil, all plugins will + # be loaded. If this is set to [], no plugins will be loaded. Otherwise, + # plugins will be loaded in the order specified. + attr_reader :plugins + def plugins=(plugins) + @plugins = plugins.nil? ? nil : plugins.map { |p| p.to_sym } + end + + # The list of metals to load. If this is set to nil, all metals will + # be loaded in alphabetical order. If this is set to [], no metals will + # be loaded. Otherwise metals will be loaded in the order specified + attr_accessor :metals + + # The path to the root of the plugins directory. By default, it is in + # vendor/plugins. + attr_accessor :plugin_paths + + # The classes that handle finding the desired plugins that you'd like to load for + # your application. By default it is the Rails::Plugin::FileSystemLocator which finds + # plugins to load in vendor/plugins. You can hook into gem location by subclassing + # Rails::Plugin::Locator and adding it onto the list of plugin_locators. + attr_accessor :plugin_locators + + # The class that handles loading each plugin. Defaults to Rails::Plugin::Loader, but + # a sub class would have access to fine grained modification of the loading behavior. See + # the implementation of Rails::Plugin::Loader for more details. + attr_accessor :plugin_loader + + # Enables or disables plugin reloading. You can get around this setting per plugin. + # If reload_plugins? is false, add this to your plugin's init.rb + # to make it reloadable: + # + # ActiveSupport::Dependencies.load_once_paths.delete lib_path + # + # If reload_plugins? is true, add this to your plugin's init.rb + # to only load it once: + # + # ActiveSupport::Dependencies.load_once_paths << lib_path + # + attr_accessor :reload_plugins + + # Returns true if plugin reloading is enabled. + def reload_plugins? + !!@reload_plugins + end + + # Enables or disables dependency loading during the request cycle. Setting + # dependency_loading to true will allow new classes to be loaded + # during a request. Setting it to false will disable this behavior. + # + # Those who want to run in a threaded environment should disable this + # option and eager load or require all there classes on initialization. + # + # If cache_classes is disabled, dependency loaded will always be + # on. + attr_accessor :dependency_loading + + # An array of gems that this rails application depends on. Rails will automatically load + # these gems during installation, and allow you to install any missing gems with: + # + # rake gems:install + # + # You can add gems with the #gem method. + attr_accessor :gems + + # Adds a single Gem dependency to the rails application. By default, it will require + # the library with the same name as the gem. Use :lib to specify a different name. + # + # # gem 'aws-s3', '>= 0.4.0' + # # require 'aws/s3' + # config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \ + # :source => "http://code.whytheluckystiff.net" + # + # To require a library be installed, but not attempt to load it, pass :lib => false + # + # config.gem 'qrp', :version => '0.4.1', :lib => false + def gem(name, options = {}) + @gems << Rails::GemDependency.new(name, options) + end + + # Deprecated options: + def breakpoint_server(_ = nil) + $stderr.puts %( + ******************************************************************* + * config.breakpoint_server has been deprecated and has no effect. * + ******************************************************************* + ) + end + alias_method :breakpoint_server=, :breakpoint_server + + # Sets the default +time_zone+. Setting this will enable +time_zone+ + # awareness for Active Record models and set the Active Record default + # timezone to :utc. + attr_accessor :time_zone + + # Accessor for i18n settings. + attr_accessor :i18n + + # Create a new Configuration instance, initialized with the default + # values. + def initialize + set_root_path! + + self.frameworks = default_frameworks + self.load_paths = default_load_paths + self.load_once_paths = default_load_once_paths + self.eager_load_paths = default_eager_load_paths + self.log_path = default_log_path + self.log_level = default_log_level + self.view_path = default_view_path + self.controller_paths = default_controller_paths + self.preload_frameworks = default_preload_frameworks + self.cache_classes = default_cache_classes + self.dependency_loading = default_dependency_loading + self.whiny_nils = default_whiny_nils + self.plugins = default_plugins + self.plugin_paths = default_plugin_paths + self.plugin_locators = default_plugin_locators + self.plugin_loader = default_plugin_loader + self.database_configuration_file = default_database_configuration_file + self.routes_configuration_file = default_routes_configuration_file + self.gems = default_gems + self.i18n = default_i18n + + for framework in default_frameworks + self.send("#{framework}=", Rails::OrderedOptions.new) + end + self.active_support = Rails::OrderedOptions.new + end + + # Set the root_path to RAILS_ROOT and canonicalize it. + def set_root_path! + raise 'RAILS_ROOT is not set' unless defined?(::RAILS_ROOT) + raise 'RAILS_ROOT is not a directory' unless File.directory?(::RAILS_ROOT) + + @root_path = + # Pathname is incompatible with Windows, but Windows doesn't have + # real symlinks so File.expand_path is safe. + if RUBY_PLATFORM =~ /(:?mswin|mingw)/ + File.expand_path(::RAILS_ROOT) + + # Otherwise use Pathname#realpath which respects symlinks. + else + Pathname.new(::RAILS_ROOT).realpath.to_s + end + + Object.const_set(:RELATIVE_RAILS_ROOT, ::RAILS_ROOT.dup) unless defined?(::RELATIVE_RAILS_ROOT) + ::RAILS_ROOT.replace @root_path + end + + # Enable threaded mode. Allows concurrent requests to controller actions and + # multiple database connections. Also disables automatic dependency loading + # after boot, and disables reloading code on every request, as these are + # fundamentally incompatible with thread safety. + def threadsafe! + self.preload_frameworks = true + self.cache_classes = true + self.dependency_loading = false + self.action_controller.allow_concurrency = true + self + end + + # Loads and returns the contents of the #database_configuration_file. The + # contents of the file are processed via ERB before being sent through + # YAML::load. + def database_configuration + require 'erb' + YAML::load(ERB.new(IO.read(database_configuration_file)).result) + end + + # The path to the current environment's file (development.rb, etc.). By + # default the file is at config/environments/#{environment}.rb. + def environment_path + "#{root_path}/config/environments/#{environment}.rb" + end + + # Return the currently selected environment. By default, it returns the + # value of the RAILS_ENV constant. + def environment + ::RAILS_ENV + end + + # Adds a block which will be executed after rails has been fully initialized. + # Useful for per-environment configuration which depends on the framework being + # fully initialized. + def after_initialize(&after_initialize_block) + after_initialize_blocks << after_initialize_block if after_initialize_block + end + + # Returns the blocks added with Configuration#after_initialize + def after_initialize_blocks + @after_initialize_blocks ||= [] + end + + # Add a preparation callback that will run before every request in development + # mode, or before the first request in production. + # + # See Dispatcher#to_prepare. + def to_prepare(&callback) + after_initialize do + require 'dispatcher' unless defined?(::Dispatcher) + Dispatcher.to_prepare(&callback) + end + end + + def middleware + require 'action_controller' + ActionController::Dispatcher.middleware + end + + def builtin_directories + # Include builtins only in the development environment. + (environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : [] + end + + def framework_paths + paths = %w(railties railties/lib activesupport/lib) + paths << 'actionpack/lib' if frameworks.include?(:action_controller) || frameworks.include?(:action_view) + + [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework| + paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include?(framework) + end + + paths.map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + end + + private + def framework_root_path + defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root_path}/vendor/rails" + end + + def default_frameworks + [ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ] + end + + def default_load_paths + paths = [] + + # Add the old mock paths only if the directories exists + paths.concat(Dir["#{root_path}/test/mocks/#{environment}"]) if File.exists?("#{root_path}/test/mocks/#{environment}") + + # Add the app's controller directory + paths.concat(Dir["#{root_path}/app/controllers/"]) + + # Followed by the standard includes. + paths.concat %w( + app + app/metal + app/models + app/controllers + app/helpers + app/services + lib + vendor + ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + + paths.concat builtin_directories + end + + # Doesn't matter since plugins aren't in load_paths yet. + def default_load_once_paths + [] + end + + def default_eager_load_paths + %w( + app/metal + app/models + app/controllers + app/helpers + ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + end + + def default_log_path + File.join(root_path, 'log', "#{environment}.log") + end + + def default_log_level + environment == 'production' ? :info : :debug + end + + def default_database_configuration_file + File.join(root_path, 'config', 'database.yml') + end + + def default_routes_configuration_file + File.join(root_path, 'config', 'routes.rb') + end + + def default_view_path + File.join(root_path, 'app', 'views') + end + + def default_controller_paths + paths = [File.join(root_path, 'app', 'controllers')] + paths.concat builtin_directories + paths + end + + def default_dependency_loading + true + end + + def default_preload_frameworks + false + end + + def default_cache_classes + true + end + + def default_whiny_nils + false + end + + def default_plugins + nil + end + + def default_plugin_paths + ["#{root_path}/vendor/plugins"] + end + + def default_plugin_locators + locators = [] + locators << Plugin::GemLocator if defined? Gem + locators << Plugin::FileSystemLocator + end + + def default_plugin_loader + Plugin::Loader + end + + def default_cache_store + if File.exist?("#{root_path}/tmp/cache/") + [ :file_store, "#{root_path}/tmp/cache/" ] + else + :memory_store + end + end + + def default_gems + [] + end + + def default_i18n + i18n = Rails::OrderedOptions.new + i18n.load_path = [] + + if File.exist?(File.join(RAILS_ROOT, 'config', 'locales')) + i18n.load_path << Dir[File.join(RAILS_ROOT, 'config', 'locales', '*.{rb,yml}')] + i18n.load_path.flatten! + end + + i18n + end + end +end + +# Needs to be duplicated from Active Support since its needed before Active +# Support is available. Here both Options and Hash are namespaced to prevent +# conflicts with other implementations AND with the classes residing in Active Support. +class Rails::OrderedOptions < Array #:nodoc: + def []=(key, value) + key = key.to_sym + + if pair = find_pair(key) + pair.pop + pair << value + else + self << [key, value] + end + end + + def [](key) + pair = find_pair(key.to_sym) + pair ? pair.last : nil + end + + def method_missing(name, *args) + if name.to_s =~ /(.*)=$/ + self[$1.to_sym] = args.first + else + self[name] + end + end + + private + def find_pair(key) + self.each { |i| return i if i.first == key } + return false + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/performance_test_help.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/performance_test_help.rb new file mode 100644 index 00000000000..5148b4ab77c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/performance_test_help.rb @@ -0,0 +1,5 @@ +require 'action_controller/performance_test' + +ActionController::Base.perform_caching = true +ActiveSupport::Dependencies.mechanism = :require +Rails.logger.level = ActiveSupport::BufferedLogger::INFO diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/backtrace_cleaner.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/backtrace_cleaner.rb new file mode 100644 index 00000000000..923ed8b31dd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/backtrace_cleaner.rb @@ -0,0 +1,54 @@ +module Rails + class BacktraceCleaner < ActiveSupport::BacktraceCleaner + ERB_METHOD_SIG = /:in `_run_erb_.*/ + + RAILS_GEMS = %w( actionpack activerecord actionmailer activesupport activeresource rails ) + + VENDOR_DIRS = %w( vendor/rails ) + SERVER_DIRS = %w( lib/mongrel bin/mongrel + lib/passenger bin/passenger-spawn-server + lib/rack ) + RAILS_NOISE = %w( script/server ) + RUBY_NOISE = %w( rubygems/custom_require benchmark.rb ) + + ALL_NOISE = VENDOR_DIRS + SERVER_DIRS + RAILS_NOISE + RUBY_NOISE + + def initialize + super + add_filter { |line| line.sub("#{RAILS_ROOT}/", '') } + add_filter { |line| line.sub(ERB_METHOD_SIG, '') } + add_filter { |line| line.sub('./', '/') } # for tests + + add_gem_filters + + add_silencer { |line| ALL_NOISE.any? { |dir| line.include?(dir) } } + add_silencer { |line| RAILS_GEMS.any? { |gem| line =~ /^#{gem} / } } + add_silencer { |line| line =~ %r(vendor/plugins/[^\/]+/lib) } + end + + + private + def add_gem_filters + Gem.path.each do |path| + # http://gist.github.com/30430 + add_filter { |line| line.sub(/(#{path})\/gems\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) \4')} + end + + vendor_gems_path = Rails::GemDependency.unpacked_path.sub("#{RAILS_ROOT}/",'') + add_filter { |line| line.sub(/(#{vendor_gems_path})\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) [v] \4')} + end + end + + # For installing the BacktraceCleaner in the test/unit + module BacktraceFilterForTestUnit #:nodoc: + def self.included(klass) + klass.send :alias_method_chain, :filter_backtrace, :cleaning + end + + def filter_backtrace_with_cleaning(backtrace, prefix=nil) + backtrace = filter_backtrace_without_cleaning(backtrace, prefix) + backtrace = backtrace.first.split("\n") if backtrace.size == 1 + Rails.backtrace_cleaner.clean(backtrace) + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/gem_builder.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/gem_builder.rb new file mode 100644 index 00000000000..79c61cc0344 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/gem_builder.rb @@ -0,0 +1,21 @@ +require 'rubygems' +require 'rubygems/installer' + +module Rails + + # this class hijacks the functionality of Gem::Installer by overloading its + # initializer to only provide the information needed by + # Gem::Installer#build_extensions (which happens to be what we have) + class GemBuilder < Gem::Installer + + def initialize(spec, gem_dir) + @spec = spec + @gem_dir = gem_dir + end + + # silence the underlying builder + def say(message) + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/gem_dependency.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/gem_dependency.rb new file mode 100644 index 00000000000..06d830ba24a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/gem_dependency.rb @@ -0,0 +1,311 @@ +require 'rails/vendor_gem_source_index' + +module Gem + def self.source_index=(index) + @@source_index = index + end +end + +module Rails + class GemDependency < Gem::Dependency + attr_accessor :lib, :source, :dep + + def self.unpacked_path + @unpacked_path ||= File.join(RAILS_ROOT, 'vendor', 'gems') + end + + @@framework_gems = {} + + def self.add_frozen_gem_path + @@paths_loaded ||= begin + source_index = Rails::VendorGemSourceIndex.new(Gem.source_index) + Gem.clear_paths + Gem.source_index = source_index + # loaded before us - we can't change them, so mark them + Gem.loaded_specs.each do |name, spec| + @@framework_gems[name] = spec + end + true + end + end + + def self.from_directory_name(directory_name, load_spec=true) + directory_name_parts = File.basename(directory_name).split('-') + name = directory_name_parts[0..-2].join('-') + version = directory_name_parts.last + result = self.new(name, :version => version) + spec_filename = File.join(directory_name, '.specification') + if load_spec + raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'?" unless File.exists?(spec_filename) + spec = YAML::load_file(spec_filename) + result.specification = spec + end + result + rescue ArgumentError => e + raise "Unable to determine gem name and version from '#{directory_name}'" + end + + def initialize(name, options = {}) + require 'rubygems' unless Object.const_defined?(:Gem) + + if options[:requirement] + req = options[:requirement] + elsif options[:version] + req = Gem::Requirement.create(options[:version]) + else + req = Gem::Requirement.default + end + + @lib = options[:lib] + @source = options[:source] + @loaded = @frozen = @load_paths_added = false + + super(name, req) + end + + def add_load_paths + self.class.add_frozen_gem_path + return if @loaded || @load_paths_added + if framework_gem? + @load_paths_added = @loaded = @frozen = true + return + end + gem self + @spec = Gem.loaded_specs[name] + @frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec + @load_paths_added = true + rescue Gem::LoadError + end + + def dependencies + return [] if framework_gem? + return [] unless installed? + specification.dependencies.reject do |dependency| + dependency.type == :development + end.map do |dependency| + GemDependency.new(dependency.name, :requirement => dependency.version_requirements) + end + end + + def specification + # code repeated from Gem.activate. Find a matching spec, or the currently loaded version. + # error out if loaded version and requested version are incompatible. + @spec ||= begin + matches = Gem.source_index.search(self) + matches << @@framework_gems[name] if framework_gem? + if Gem.loaded_specs[name] then + # This gem is already loaded. If the currently loaded gem is not in the + # list of candidate gems, then we have a version conflict. + existing_spec = Gem.loaded_specs[name] + unless matches.any? { |spec| spec.version == existing_spec.version } then + raise Gem::Exception, + "can't activate #{@dep}, already activated #{existing_spec.full_name}" + end + # we're stuck with it, so change to match + version_requirements = Gem::Requirement.create("=#{existing_spec.version}") + existing_spec + else + # new load + matches.last + end + end + end + + def specification=(s) + @spec = s + end + + def requirement + r = version_requirements + (r == Gem::Requirement.default) ? nil : r + end + + def built? + return false unless frozen? + + if vendor_gem? + specification.extensions.each do |ext| + makefile = File.join(unpacked_gem_directory, File.dirname(ext), 'Makefile') + return false unless File.exists?(makefile) + end + end + + true + end + + def framework_gem? + @@framework_gems.has_key?(name) + end + + def frozen? + @frozen ||= vendor_rails? || vendor_gem? + end + + def installed? + Gem.loaded_specs.keys.include?(name) + end + + def load_paths_added? + # always try to add load paths - even if a gem is loaded, it may not + # be a compatible version (ie random_gem 0.4 is loaded and a later spec + # needs >= 0.5 - gem 'random_gem' will catch this and error out) + @load_paths_added + end + + def loaded? + @loaded ||= begin + if vendor_rails? + true + elsif specification.nil? + false + else + # check if the gem is loaded by inspecting $" + # specification.files lists all the files contained in the gem + gem_files = specification.files + # select only the files contained in require_paths - typically in bin and lib + require_paths_regexp = Regexp.new("^(#{specification.require_paths*'|'})/") + gem_lib_files = gem_files.select { |f| require_paths_regexp.match(f) } + # chop the leading directory off - a typical file might be in + # lib/gem_name/file_name.rb, but it will be 'require'd as gem_name/file_name.rb + gem_lib_files.map! { |f| f.split('/', 2)[1] } + # if any of the files from the above list appear in $", the gem is assumed to + # have been loaded + !(gem_lib_files & $").empty? + end + end + end + + def vendor_rails? + Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.empty? + end + + def vendor_gem? + specification && File.exists?(unpacked_gem_directory) + end + + def build(options={}) + require 'rails/gem_builder' + return if specification.nil? + if options[:force] || !built? + return unless File.exists?(unpacked_specification_filename) + spec = YAML::load_file(unpacked_specification_filename) + Rails::GemBuilder.new(spec, unpacked_gem_directory).build_extensions + puts "Built gem: '#{unpacked_gem_directory}'" + end + dependencies.each { |dep| dep.build(options) } + end + + def install + unless installed? + cmd = "#{gem_command} #{install_command.join(' ')}" + puts cmd + puts %x(#{cmd}) + end + end + + def load + return if @loaded || @load_paths_added == false + require(@lib || name) unless @lib == false + @loaded = true + rescue LoadError + puts $!.to_s + $!.backtrace.each { |b| puts b } + end + + def refresh + Rails::VendorGemSourceIndex.silence_spec_warnings = true + real_gems = Gem.source_index.installed_source_index + exact_dep = Gem::Dependency.new(name, "= #{specification.version}") + matches = real_gems.search(exact_dep) + installed_spec = matches.first + if frozen? + if installed_spec + # we have a real copy + # get a fresh spec - matches should only have one element + # note that there is no reliable method to check that the loaded + # spec is the same as the copy from real_gems - Gem.activate changes + # some of the fields + real_spec = Gem::Specification.load(matches.first.loaded_from) + write_specification(real_spec) + puts "Reloaded specification for #{name} from installed gems." + else + # the gem isn't installed locally - write out our current specs + write_specification(specification) + puts "Gem #{name} not loaded locally - writing out current spec." + end + else + if framework_gem? + puts "Gem directory for #{name} not found - check if it's loading before rails." + else + puts "Something bad is going on - gem directory not found for #{name}." + end + end + end + + def unpack(options={}) + unless frozen? || framework_gem? + FileUtils.mkdir_p unpack_base + Dir.chdir unpack_base do + Gem::GemRunner.new.run(unpack_command) + end + # Gem.activate changes the spec - get the original + real_spec = Gem::Specification.load(specification.loaded_from) + write_specification(real_spec) + end + dependencies.each { |dep| dep.unpack(options) } if options[:recursive] + end + + def write_specification(spec) + # copy the gem's specification into GEMDIR/.specification so that + # we can access information about the gem on deployment systems + # without having the gem installed + File.open(unpacked_specification_filename, 'w') do |file| + file.puts spec.to_yaml + end + end + + def ==(other) + self.name == other.name && self.requirement == other.requirement + end + alias_method :"eql?", :"==" + + private + + def gem_command + case RUBY_PLATFORM + when /win32/ + 'gem.bat' + when /java/ + 'jruby -S gem' + else + 'gem' + end + end + + def install_command + cmd = %w(install) << name + cmd << "--version" << %("#{requirement.to_s}") if requirement + cmd << "--source" << @source if @source + cmd + end + + def unpack_command + cmd = %w(unpack) << name + cmd << "--version" << "= "+specification.version.to_s if requirement + cmd + end + + def unpack_base + Rails::GemDependency.unpacked_path + end + + def unpacked_gem_directory + File.join(unpack_base, specification.full_name) + end + + def unpacked_specification_filename + File.join(unpacked_gem_directory, '.specification') + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin.rb new file mode 100644 index 00000000000..1dc4d142c5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin.rb @@ -0,0 +1,179 @@ +module Rails + # The Plugin class should be an object which provides the following methods: + # + # * +name+ - Used during initialisation to order the plugin (based on name and + # the contents of config.plugins). + # * +valid?+ - Returns true if this plugin can be loaded. + # * +load_paths+ - Each path within the returned array will be added to the $LOAD_PATH. + # * +load+ - Finally 'load' the plugin. + # + # These methods are expected by the Rails::Plugin::Locator and Rails::Plugin::Loader classes. + # The default implementation returns the lib directory as its load_paths, + # and evaluates init.rb when load is called. + # + # You can also inspect the about.yml data programmatically: + # + # plugin = Rails::Plugin.new(path_to_my_plugin) + # plugin.about["author"] # => "James Adam" + # plugin.about["url"] # => "http://interblah.net" + class Plugin + include Comparable + + attr_reader :directory, :name + + def initialize(directory) + @directory = directory + @name = File.basename(@directory) rescue nil + @loaded = false + end + + def valid? + File.directory?(directory) && (has_app_directory? || has_lib_directory? || has_init_file?) + end + + # Returns a list of paths this plugin wishes to make available in $LOAD_PATH. + def load_paths + report_nonexistant_or_empty_plugin! unless valid? + + returning [] do |load_paths| + load_paths << lib_path if has_lib_directory? + load_paths << app_paths if has_app_directory? + end.flatten + end + + # Evaluates a plugin's init.rb file. + def load(initializer) + return if loaded? + report_nonexistant_or_empty_plugin! unless valid? + evaluate_init_rb(initializer) + @loaded = true + end + + def loaded? + @loaded + end + + def <=>(other_plugin) + name <=> other_plugin.name + end + + def about + @about ||= load_about_information + end + + # Engines are plugins with an app/ directory. + def engine? + has_app_directory? + end + + # Returns true if the engine ships with a routing file + def routed? + File.exist?(routing_file) + end + + # Returns true if there is any localization file in locale_path + def localized? + locale_files.any? + end + + def view_path + File.join(directory, 'app', 'views') + end + + def controller_path + File.join(directory, 'app', 'controllers') + end + + def metal_path + File.join(directory, 'app', 'metal') + end + + def routing_file + File.join(directory, 'config', 'routes.rb') + end + + def locale_path + File.join(directory, 'config', 'locales') + end + + def locale_files + Dir[ File.join(locale_path, '*.{rb,yml}') ] + end + + + private + def load_about_information + about_yml_path = File.join(@directory, "about.yml") + parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {} + parsed_yml || {} + rescue Exception + {} + end + + def report_nonexistant_or_empty_plugin! + raise LoadError, "Can not find the plugin named: #{name}" + end + + + def app_paths + [ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path, metal_path ] + end + + def lib_path + File.join(directory, 'lib') + end + + def classic_init_path + File.join(directory, 'init.rb') + end + + def gem_init_path + File.join(directory, 'rails', 'init.rb') + end + + def init_path + File.file?(gem_init_path) ? gem_init_path : classic_init_path + end + + + def has_app_directory? + File.directory?(File.join(directory, 'app')) + end + + def has_lib_directory? + File.directory?(lib_path) + end + + def has_init_file? + File.file?(init_path) + end + + + def evaluate_init_rb(initializer) + if has_init_file? + silence_warnings do + # Allow plugins to reference the current configuration object + config = initializer.configuration + + eval(IO.read(init_path), binding, init_path) + end + end + end + end + + # This Plugin subclass represents a Gem plugin. Although RubyGems has already + # taken care of $LOAD_PATHs, it exposes its load_paths to add them + # to Dependencies.load_paths. + class GemPlugin < Plugin + # Initialize this plugin from a Gem::Specification. + def initialize(spec, gem) + directory = spec.full_gem_path + super(directory) + @name = spec.name + end + + def init_path + File.join(directory, 'rails', 'init.rb') + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin/loader.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin/loader.rb new file mode 100644 index 00000000000..49670b31e6b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin/loader.rb @@ -0,0 +1,198 @@ +require "rails/plugin" + +module Rails + class Plugin + class Loader + attr_reader :initializer + + # Creates a new Plugin::Loader instance, associated with the given + # Rails::Initializer. This default implementation automatically locates + # all plugins, and adds all plugin load paths, when it is created. The plugins + # are then fully loaded (init.rb is evaluated) when load_plugins is called. + # + # It is the loader's responsibility to ensure that only the plugins specified + # in the configuration are actually loaded, and that the order defined + # is respected. + def initialize(initializer) + @initializer = initializer + end + + # Returns the plugins to be loaded, in the order they should be loaded. + def plugins + @plugins ||= all_plugins.select { |plugin| should_load?(plugin) }.sort { |p1, p2| order_plugins(p1, p2) } + end + + # Returns the plugins that are in engine-form (have an app/ directory) + def engines + @engines ||= plugins.select(&:engine?) + end + + # Returns all the plugins that could be found by the current locators. + def all_plugins + @all_plugins ||= locate_plugins + @all_plugins + end + + def load_plugins + plugins.each do |plugin| + plugin.load(initializer) + register_plugin_as_loaded(plugin) + end + + configure_engines + + ensure_all_registered_plugins_are_loaded! + end + + # Adds the load paths for every plugin into the $LOAD_PATH. Plugin load paths are + # added *after* the application's lib directory, to ensure that an application + # can always override code within a plugin. + # + # Plugin load paths are also added to Dependencies.load_paths, and Dependencies.load_once_paths. + def add_plugin_load_paths + plugins.each do |plugin| + plugin.load_paths.each do |path| + $LOAD_PATH.insert(application_lib_index + 1, path) + + ActiveSupport::Dependencies.load_paths << path + + unless configuration.reload_plugins? + ActiveSupport::Dependencies.load_once_paths << path + end + end + end + + $LOAD_PATH.uniq! + end + + def engine_metal_paths + engines.collect(&:metal_path) + end + + protected + def configure_engines + if engines.any? + add_engine_routing_configurations + add_engine_locales + add_engine_controller_paths + add_engine_view_paths + end + end + + def add_engine_routing_configurations + engines.select(&:routed?).collect(&:routing_file).each do |routing_file| + ActionController::Routing::Routes.add_configuration_file(routing_file) + end + end + + def add_engine_locales + # reverse it such that the last engine can overwrite translations from the first, like with routes + locale_files = engines.select(&:localized?).collect(&:locale_files).reverse.flatten + I18n.load_path += locale_files - I18n.load_path + end + + def add_engine_controller_paths + ActionController::Routing.controller_paths += engines.collect(&:controller_path) + end + + def add_engine_view_paths + # reverse it such that the last engine can overwrite view paths from the first, like with routes + paths = ActionView::PathSet.new(engines.collect(&:view_path).reverse) + ActionController::Base.view_paths.concat(paths) + ActionMailer::Base.view_paths.concat(paths) if configuration.frameworks.include?(:action_mailer) + end + + # The locate_plugins method uses each class in config.plugin_locators to + # find the set of all plugins available to this Rails application. + def locate_plugins + configuration.plugin_locators.map do |locator| + locator.new(initializer).plugins + end.flatten + # TODO: sorting based on config.plugins + end + + def register_plugin_as_loaded(plugin) + initializer.loaded_plugins << plugin + end + + def configuration + initializer.configuration + end + + def should_load?(plugin) + # uses Plugin#name and Plugin#valid? + enabled?(plugin) && plugin.valid? + end + + def order_plugins(plugin_a, plugin_b) + if !explicit_plugin_loading_order? + plugin_a <=> plugin_b + else + if !explicitly_enabled?(plugin_a) && !explicitly_enabled?(plugin_b) + plugin_a <=> plugin_b + else + effective_order_of(plugin_a) <=> effective_order_of(plugin_b) + end + end + end + + def effective_order_of(plugin) + if explicitly_enabled?(plugin) + registered_plugin_names.index(plugin.name) + else + registered_plugin_names.index('all') + end + end + + def application_lib_index + $LOAD_PATH.index(File.join(RAILS_ROOT, 'lib')) || 0 + end + + def enabled?(plugin) + !explicit_plugin_loading_order? || registered?(plugin) + end + + def explicit_plugin_loading_order? + !registered_plugin_names.nil? + end + + def registered?(plugin) + explicit_plugin_loading_order? && registered_plugins_names_plugin?(plugin) + end + + def explicitly_enabled?(plugin) + !explicit_plugin_loading_order? || explicitly_registered?(plugin) + end + + def explicitly_registered?(plugin) + explicit_plugin_loading_order? && registered_plugin_names.include?(plugin.name) + end + + def registered_plugins_names_plugin?(plugin) + registered_plugin_names.include?(plugin.name) || registered_plugin_names.include?('all') + end + + # The plugins that have been explicitly listed with config.plugins. If this list is nil + # then it means the client does not care which plugins or in what order they are loaded, + # so we load all in alphabetical order. If it is an empty array, we load no plugins, if it is + # non empty, we load the named plugins in the order specified. + def registered_plugin_names + configuration.plugins ? configuration.plugins.map(&:to_s) : nil + end + + def loaded?(plugin_name) + initializer.loaded_plugins.detect { |plugin| plugin.name == plugin_name.to_s } + end + + def ensure_all_registered_plugins_are_loaded! + if explicit_plugin_loading_order? + if configuration.plugins.detect {|plugin| plugin != :all && !loaded?(plugin) } + missing_plugins = configuration.plugins - (plugins.map{|p| p.name.to_sym} + [:all]) + raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence(:locale => :en)}" + end + end + end + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin/locator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin/locator.rb new file mode 100644 index 00000000000..a6fc388a8e7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/plugin/locator.rb @@ -0,0 +1,100 @@ +module Rails + class Plugin + + # The Plugin::Locator class should be subclasses to provide custom plugin-finding + # abilities to Rails (i.e. loading plugins from Gems, etc). Each subclass should implement + # the located_plugins method, which return an array of Plugin objects that have been found. + class Locator + include Enumerable + + attr_reader :initializer + + def initialize(initializer) + @initializer = initializer + end + + # This method should return all the plugins which this Plugin::Locator can find + # These will then be used by the current Plugin::Loader, which is responsible for actually + # loading the plugins themselves + def plugins + raise "The `plugins' method must be defined by concrete subclasses of #{self.class}" + end + + def each(&block) + plugins.each(&block) + end + + def plugin_names + plugins.map(&:name) + end + end + + # The Rails::Plugin::FileSystemLocator will try to locate plugins by examining the directories + # in the paths given in configuration.plugin_paths. Any plugins that can be found are returned + # in a list. + # + # The criteria for a valid plugin in this case is found in Rails::Plugin#valid?, although + # other subclasses of Rails::Plugin::Locator can of course use different conditions. + class FileSystemLocator < Locator + + # Returns all the plugins which can be loaded in the filesystem, under the paths given + # by configuration.plugin_paths. + def plugins + initializer.configuration.plugin_paths.flatten.inject([]) do |plugins, path| + plugins.concat locate_plugins_under(path) + plugins + end.flatten + end + + private + + # Attempts to create a plugin from the given path. If the created plugin is valid? + # (see Rails::Plugin#valid?) then the plugin instance is returned; otherwise nil. + def create_plugin(path) + plugin = Rails::Plugin.new(path) + plugin.valid? ? plugin : nil + end + + # This starts at the base path looking for valid plugins (see Rails::Plugin#valid?). + # Since plugins can be nested arbitrarily deep within an unspecified number of intermediary + # directories, this method runs recursively until it finds a plugin directory, e.g. + # + # locate_plugins_under('vendor/plugins/acts/acts_as_chunky_bacon') + # => + # + def locate_plugins_under(base_path) + Dir.glob(File.join(base_path, '*')).sort.inject([]) do |plugins, path| + if plugin = create_plugin(path) + plugins << plugin + elsif File.directory?(path) + plugins.concat locate_plugins_under(path) + end + plugins + end + end + end + + # The GemLocator scans all the loaded RubyGems, looking for gems with + # a rails/init.rb file. + class GemLocator < Locator + def plugins + gem_index = initializer.configuration.gems.inject({}) { |memo, gem| memo.update gem.specification => gem } + specs = gem_index.keys + specs += Gem.loaded_specs.values.select do |spec| + spec.loaded_from && # prune stubs + File.exist?(File.join(spec.full_gem_path, "rails", "init.rb")) + end + specs.compact! + + require "rubygems/dependency_list" + + deps = Gem::DependencyList.new + deps.add(*specs) unless specs.empty? + + deps.dependency_order.collect do |spec| + Rails::GemPlugin.new(spec, gem_index[spec]) + end + end + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack.rb new file mode 100644 index 00000000000..9705f65e52b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack.rb @@ -0,0 +1,8 @@ +module Rails + module Rack + autoload :Debugger, "rails/rack/debugger" + autoload :LogTailer, "rails/rack/log_tailer" + autoload :Metal, "rails/rack/metal" + autoload :Static, "rails/rack/static" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/debugger.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/debugger.rb new file mode 100644 index 00000000000..0a7b4055534 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/debugger.rb @@ -0,0 +1,23 @@ +module Rails + module Rack + class Debugger + def initialize(app) + @app = app + + ARGV.clear # clear ARGV so that script/server options aren't passed to IRB + + require_library_or_gem 'ruby-debug' + ::Debugger.start + ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings) + puts "=> Debugger enabled" + rescue Exception + puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'" + exit + end + + def call(env) + @app.call(env) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/log_tailer.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/log_tailer.rb new file mode 100644 index 00000000000..a237cee6bc6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/log_tailer.rb @@ -0,0 +1,35 @@ +module Rails + module Rack + class LogTailer + EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log" + + def initialize(app, log = nil) + @app = app + + path = Pathname.new(log || EnvironmentLog).cleanpath + @cursor = ::File.size(path) + @last_checked = Time.now.to_f + + @file = ::File.open(path, 'r') + end + + def call(env) + response = @app.call(env) + tail_log + response + end + + def tail_log + @file.seek @cursor + + mod = @file.mtime.to_f + if mod > @last_checked + contents = @file.read + @last_checked = mod + @cursor += contents.size + $stdout.print contents + end + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/metal.rb new file mode 100644 index 00000000000..8719a5c5812 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/metal.rb @@ -0,0 +1,51 @@ +require 'active_support/ordered_hash' + +module Rails + module Rack + class Metal + NotFoundResponse = [404, {}, []].freeze + NotFound = lambda { NotFoundResponse } + + cattr_accessor :metal_paths + self.metal_paths = ["#{Rails.root}/app/metal"] + cattr_accessor :requested_metals + + def self.metals + matcher = /#{Regexp.escape('/app/metal/')}(.*)\.rb\Z/ + metal_glob = metal_paths.map{ |base| "#{base}/**/*.rb" } + all_metals = {} + + metal_glob.each do |glob| + Dir[glob].sort.map do |file| + file = file.match(matcher)[1] + all_metals[file.camelize] = file + end + end + + load_list = requested_metals || all_metals.keys + + load_list.map do |requested_metal| + if metal = all_metals[requested_metal] + require_dependency metal + requested_metal.constantize + end + end.compact + end + + def initialize(app) + @app = app + @metals = ActiveSupport::OrderedHash.new + self.class.metals.each { |app| @metals[app] = true } + freeze + end + + def call(env) + @metals.keys.each do |app| + result = app.call(env) + return result unless result[0].to_i == 404 + end + @app.call(env) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/static.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/static.rb new file mode 100644 index 00000000000..f07c6beb5e4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/rack/static.rb @@ -0,0 +1,46 @@ +require 'rack/utils' + +module Rails + module Rack + class Static + FILE_METHODS = %w(GET HEAD).freeze + + def initialize(app) + @app = app + @file_server = ::Rack::File.new(File.join(RAILS_ROOT, "public")) + end + + def call(env) + path = env['PATH_INFO'].chomp('/') + method = env['REQUEST_METHOD'] + + if FILE_METHODS.include?(method) + if file_exist?(path) + return @file_server.call(env) + else + cached_path = directory_exist?(path) ? "#{path}/index" : path + cached_path += ::ActionController::Base.page_cache_extension + + if file_exist?(cached_path) + env['PATH_INFO'] = cached_path + return @file_server.call(env) + end + end + end + + @app.call(env) + end + + private + def file_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.file?(full_path) && File.readable?(full_path) + end + + def directory_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.directory?(full_path) && File.readable?(full_path) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/vendor_gem_source_index.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/vendor_gem_source_index.rb new file mode 100644 index 00000000000..5b7721f303f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/vendor_gem_source_index.rb @@ -0,0 +1,140 @@ +require 'rubygems' +require 'yaml' + +module Rails + + class VendorGemSourceIndex + # VendorGemSourceIndex acts as a proxy for the Gem source index, allowing + # gems to be loaded from vendor/gems. Rather than the standard gem repository format, + # vendor/gems contains unpacked gems, with YAML specifications in .specification in + # each gem directory. + include Enumerable + + attr_reader :installed_source_index + attr_reader :vendor_source_index + + @@silence_spec_warnings = false + + def self.silence_spec_warnings + @@silence_spec_warnings + end + + def self.silence_spec_warnings=(v) + @@silence_spec_warnings = v + end + + def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path) + @installed_source_index = installed_index + @vendor_dir = vendor_dir + refresh! + end + + def refresh! + # reload the installed gems + @installed_source_index.refresh! + vendor_gems = {} + + # handle vendor Rails gems - they are identified by having loaded_from set to "" + # we add them manually to the list, so that other gems can find them via dependencies + Gem.loaded_specs.each do |n, s| + next unless s.loaded_from.empty? + vendor_gems[s.full_name] = s + end + + # load specifications from vendor/gems + Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d| + dir_name = File.basename(d) + dir_version = version_for_dir(dir_name) + spec = load_specification(d) + if spec + if spec.full_name != dir_name + # mismatched directory name and gem spec - produced by 2.1.0-era unpack code + if dir_version + # fix the spec version - this is not optimal (spec.files may be wrong) + # but it's better than breaking apps. Complain to remind users to get correct specs. + # use ActiveSupport::Deprecation.warn, as the logger is not set yet + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has a mismatched specification file."+ + " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings + spec.version = dir_version + else + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems is not in a versioned directory"+ + "(should be #{spec.full_name}).") unless @@silence_spec_warnings + # continue, assume everything is OK + end + end + else + # no spec - produced by early-2008 unpack code + # emulate old behavior, and complain. + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has no specification file."+ + " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings + if dir_version + spec = Gem::Specification.new + spec.version = dir_version + spec.require_paths = ['lib'] + ext_path = File.join(d, 'ext') + spec.require_paths << 'ext' if File.exist?(ext_path) + spec.name = /^(.*)-[^-]+$/.match(dir_name)[1] + files = ['lib'] + # set files to everything in lib/ + files += Dir[File.join(d, 'lib', '*')].map { |v| v.gsub(/^#{d}\//, '') } + files += Dir[File.join(d, 'ext', '*')].map { |v| v.gsub(/^#{d}\//, '') } if ext_path + spec.files = files + else + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+ + " Giving up.") unless @@silence_spec_warnings + next + end + end + spec.loaded_from = File.join(d, '.specification') + # finally, swap out full_gem_path + # it would be better to use a Gem::Specification subclass, but the YAML loads an explicit class + class << spec + def full_gem_path + path = File.join installation_path, full_name + return path if File.directory? path + File.join installation_path, original_name + end + end + vendor_gems[File.basename(d)] = spec + end + @vendor_source_index = Gem::SourceIndex.new(vendor_gems) + end + + def version_for_dir(d) + matches = /-([^-]+)$/.match(d) + Gem::Version.new(matches[1]) if matches + end + + def load_specification(gem_dir) + spec_file = File.join(gem_dir, '.specification') + YAML.load_file(spec_file) if File.exist?(spec_file) + end + + def find_name(*args) + @installed_source_index.find_name(*args) + @vendor_source_index.find_name(*args) + end + + def search(*args) + # look for vendor gems, and then installed gems - later elements take priority + @installed_source_index.search(*args) + @vendor_source_index.search(*args) + end + + def each(&block) + @vendor_source_index.each(&block) + @installed_source_index.each(&block) + end + + def add_spec(spec) + @vendor_source_index.add_spec spec + end + + def remove_spec(spec) + @vendor_source_index.remove_spec spec + end + + def size + @vendor_source_index.size + @installed_source_index.size + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/version.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/version.rb new file mode 100644 index 00000000000..98e4e03a830 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails/version.rb @@ -0,0 +1,9 @@ +module Rails + module VERSION #:nodoc: + MAJOR = 2 + MINOR = 3 + TINY = 5 + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator.rb new file mode 100644 index 00000000000..9f0ffc1562c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator.rb @@ -0,0 +1,43 @@ +#-- +# Copyright (c) 2004 Jeremy Kemper +# +# 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. +#++ + +$:.unshift(File.dirname(__FILE__)) +$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib") + +begin + require 'active_support' +rescue LoadError + require 'rubygems' + gem 'activesupport' +end + +require 'rails_generator/base' +require 'rails_generator/lookup' +require 'rails_generator/commands' + +Rails::Generator::Base.send(:include, Rails::Generator::Lookup) +Rails::Generator::Base.send(:include, Rails::Generator::Commands) + +# Set up a default logger for convenience. +require 'rails_generator/simple_logger' +Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(STDOUT) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/base.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/base.rb new file mode 100644 index 00000000000..dd75f05c7bd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/base.rb @@ -0,0 +1,266 @@ +require File.dirname(__FILE__) + '/options' +require File.dirname(__FILE__) + '/manifest' +require File.dirname(__FILE__) + '/spec' +require File.dirname(__FILE__) + '/generated_attribute' + +module Rails + # Rails::Generator is a code generation platform tailored for the Rails + # web application framework. Generators are easily invoked within Rails + # applications to add and remove components such as models and controllers. + # New generators are easy to create and may be distributed as RubyGems, + # tarballs, or Rails plugins for inclusion system-wide, per-user, + # or per-application. + # + # For actual examples see the rails_generator/generators directory in the + # Rails source (or the +railties+ directory if you have frozen the Rails + # source in your application). + # + # Generators may subclass other generators to provide variations that + # require little or no new logic but replace the template files. + # + # For a RubyGem, put your generator class and templates in the +lib+ + # directory. For a Rails plugin, make a +generators+ directory at the + # root of your plugin. + # + # The layout of generator files can be seen in the built-in + # +controller+ generator: + # + # generators/ + # components/ + # controller/ + # controller_generator.rb + # templates/ + # controller.rb + # functional_test.rb + # helper.rb + # view.html.erb + # + # The directory name (+controller+) matches the name of the generator file + # (controller_generator.rb) and class (ControllerGenerator). The files + # that will be copied or used as templates are stored in the +templates+ + # directory. + # + # The filenames of the templates don't matter, but choose something that + # will be self-explanatory since you will be referencing these in the + # +manifest+ method inside your generator subclass. + # + # + module Generator + class GeneratorError < StandardError; end + class UsageError < GeneratorError; end + + + # The base code generator is bare-bones. It sets up the source and + # destination paths and tells the logger whether to keep its trap shut. + # + # It's useful for copying files such as stylesheets, images, or + # javascripts. + # + # For more comprehensive template-based passive code generation with + # arguments, you'll want Rails::Generator::NamedBase. + # + # Generators create a manifest of the actions they perform then hand + # the manifest to a command which replays the actions to do the heavy + # lifting (such as checking for existing files or creating directories + # if needed). Create, destroy, and list commands are included. Since a + # single manifest may be used by any command, creating new generators is + # as simple as writing some code templates and declaring what you'd like + # to do with them. + # + # The manifest method must be implemented by subclasses, returning a + # Rails::Generator::Manifest. The +record+ method is provided as a + # convenience for manifest creation. Example: + # + # class StylesheetGenerator < Rails::Generator::Base + # def manifest + # record do |m| + # m.directory('public/stylesheets') + # m.file('application.css', 'public/stylesheets/application.css') + # end + # end + # end + # + # See Rails::Generator::Commands::Create for a list of methods available + # to the manifest. + class Base + include Options + + # Declare default options for the generator. These options + # are inherited to subclasses. + default_options :collision => :ask, :quiet => false + + # A logger instance available everywhere in the generator. + cattr_accessor :logger + + # Every generator that is dynamically looked up is tagged with a + # Spec describing where it was found. + class_inheritable_accessor :spec + + attr_reader :source_root, :destination_root, :args + + def initialize(runtime_args, runtime_options = {}) + @args = runtime_args + parse!(@args, runtime_options) + + # Derive source and destination paths. + @source_root = options[:source] || File.join(spec.path, 'templates') + if options[:destination] + @destination_root = options[:destination] + elsif defined? ::RAILS_ROOT + @destination_root = ::RAILS_ROOT + end + + # Silence the logger if requested. + logger.quiet = options[:quiet] + + # Raise usage error if help is requested. + usage if options[:help] + end + + # Generators must provide a manifest. Use the +record+ method to create + # a new manifest and record your generator's actions. + def manifest + raise NotImplementedError, "No manifest for '#{spec.name}' generator." + end + + # Return the full path from the source root for the given path. + # Example for source_root = '/source': + # source_path('some/path.rb') == '/source/some/path.rb' + # + # The given path may include a colon ':' character to indicate that + # the file belongs to another generator. This notation allows any + # generator to borrow files from another. Example: + # source_path('model:fixture.yml') = '/model/source/path/fixture.yml' + def source_path(relative_source) + # Check whether we're referring to another generator's file. + name, path = relative_source.split(':', 2) + + # If not, return the full path to our source file. + if path.nil? + File.join(source_root, name) + + # Otherwise, ask our referral for the file. + else + # FIXME: this is broken, though almost always true. Others' + # source_root are not necessarily the templates dir. + File.join(self.class.lookup(name).path, 'templates', path) + end + end + + # Return the full path from the destination root for the given path. + # Example for destination_root = '/dest': + # destination_path('some/path.rb') == '/dest/some/path.rb' + def destination_path(relative_destination) + File.join(destination_root, relative_destination) + end + + def after_generate + end + + protected + # Convenience method for generator subclasses to record a manifest. + def record + Rails::Generator::Manifest.new(self) { |m| yield m } + end + + # Override with your own usage banner. + def banner + "Usage: #{$0} #{spec.name} [options]" + end + + # Read USAGE from file in generator base path. + def usage_message + File.read(File.join(spec.path, 'USAGE')) rescue '' + end + end + + + # The base generator for named components: models, controllers, mailers, + # etc. The target name is taken as the first argument and inflected to + # singular, plural, class, file, and table forms for your convenience. + # The remaining arguments are aliased to +actions+ as an array for + # controller and mailer convenience. + # + # Several useful local variables and methods are populated in the + # +initialize+ method. See below for a list of Attributes and + # External Aliases available to both the manifest and to all templates. + # + # If no name is provided, the generator raises a usage error with content + # optionally read from the USAGE file in the generator's base path. + # + # For example, the +controller+ generator takes the first argument as + # the name of the class and subsequent arguments as the names of + # actions to be generated: + # + # ./script/generate controller Article index new create + # + # See Rails::Generator::Base for a discussion of manifests, + # Rails::Generator::Commands::Create for methods available to the manifest, + # and Rails::Generator for a general discussion of generators. + class NamedBase < Base + attr_reader :name, :class_name, :singular_name, :plural_name, :table_name + attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth + alias_method :file_name, :singular_name + alias_method :actions, :args + + def initialize(runtime_args, runtime_options = {}) + super + + # Name argument is required. + usage if runtime_args.empty? + + @args = runtime_args.dup + base_name = @args.shift + assign_names!(base_name) + end + + protected + # Override with your own usage banner. + def banner + "Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]" + end + + def attributes + @attributes ||= @args.collect do |attribute| + Rails::Generator::GeneratedAttribute.new(*attribute.split(":")) + end + end + + + private + def assign_names!(name) + @name = name + base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name) + @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name) + @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name + if @class_nesting.empty? + @class_name = @class_name_without_nesting + else + @table_name = @class_nesting.underscore << "_" << @table_name + @class_name = "#{@class_nesting}::#{@class_name_without_nesting}" + end + @table_name.gsub! '/', '_' + end + + # Extract modules from filesystem-style or ruby-style path: + # good/fun/stuff + # Good::Fun::Stuff + # produce the same results. + def extract_modules(name) + modules = name.include?('/') ? name.split('/') : name.split('::') + name = modules.pop + path = modules.map { |m| m.underscore } + file_path = (path + [name.underscore]).join('/') + nesting = modules.map { |m| m.camelize }.join('::') + [name, path, file_path, nesting, modules.size] + end + + def inflect_names(name) + camel = name.camelize + under = camel.underscore + plural = under.pluralize + [camel, under, plural] + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/commands.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/commands.rb new file mode 100644 index 00000000000..60169705c5a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/commands.rb @@ -0,0 +1,632 @@ +require 'delegate' +require 'optparse' +require 'fileutils' +require 'tempfile' +require 'erb' + +module Rails + module Generator + module Commands + # Here's a convenient way to get a handle on generator commands. + # Command.instance('destroy', my_generator) instantiates a Destroy + # delegate of my_generator ready to do your dirty work. + def self.instance(command, generator) + const_get(command.to_s.camelize).new(generator) + end + + # Even more convenient access to commands. Include Commands in + # the generator Base class to get a nice #command instance method + # which returns a delegate for the requested command. + def self.included(base) + base.send(:define_method, :command) do |command| + Commands.instance(command, self) + end + end + + + # Generator commands delegate Rails::Generator::Base and implement + # a standard set of actions. Their behavior is defined by the way + # they respond to these actions: Create brings life; Destroy brings + # death; List passively observes. + # + # Commands are invoked by replaying (or rewinding) the generator's + # manifest of actions. See Rails::Generator::Manifest and + # Rails::Generator::Base#manifest method that generator subclasses + # are required to override. + # + # Commands allows generators to "plug in" invocation behavior, which + # corresponds to the GoF Strategy pattern. + class Base < DelegateClass(Rails::Generator::Base) + # Replay action manifest. RewindBase subclass rewinds manifest. + def invoke! + manifest.replay(self) + after_generate + end + + def dependency(generator_name, args, runtime_options = {}) + logger.dependency(generator_name) do + self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke! + end + end + + # Does nothing for all commands except Create. + def class_collisions(*class_names) + end + + # Does nothing for all commands except Create. + def readme(*args) + end + + protected + def current_migration_number + Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path| + n = File.basename(file_path).split('_', 2).first.to_i + if n > max then n else max end + end + end + + def next_migration_number + current_migration_number + 1 + end + + def migration_directory(relative_path) + directory(@migration_directory = relative_path) + end + + def existing_migrations(file_name) + Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/) + end + + def migration_exists?(file_name) + not existing_migrations(file_name).empty? + end + + def next_migration_string(padding = 3) + if ActiveRecord::Base.timestamped_migrations + Time.now.utc.strftime("%Y%m%d%H%M%S") + else + "%.#{padding}d" % next_migration_number + end + end + + def gsub_file(relative_destination, regexp, *args, &block) + path = destination_path(relative_destination) + content = File.read(path).gsub(regexp, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + + private + # Ask the user interactively whether to force collision. + def force_file_collision?(destination, src, dst, file_options = {}, &block) + $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] " + case $stdin.gets.chomp + when /\Ad\z/i + Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp| + temp.write render_file(src, file_options, &block) + temp.rewind + $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"` + end + puts "retrying" + raise 'retry diff' + when /\Aa\z/i + $stdout.puts "forcing #{spec.name}" + options[:collision] = :force + when /\Aq\z/i + $stdout.puts "aborting #{spec.name}" + raise SystemExit + when /\An\z/i then :skip + when /\Ay\z/i then :force + else + $stdout.puts <<-HELP +Y - yes, overwrite +n - no, do not overwrite +a - all, overwrite this and all others +q - quit, abort +d - diff, show the differences between the old and the new +h - help, show this help +HELP + raise 'retry' + end + rescue + retry + end + + def diff_cmd + ENV['RAILS_DIFF'] || 'diff -u' + end + + def render_template_part(template_options) + # Getting Sandbox to evaluate part template in it + part_binding = template_options[:sandbox].call.sandbox_binding + part_rel_path = template_options[:insert] + part_path = source_path(part_rel_path) + + # Render inner template within Sandbox binding + rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding) + begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id]) + end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id]) + begin_mark + rendered_part + end_mark + end + + def template_part_mark(name, id) + "\n" + end + end + + # Base class for commands which handle generator actions in reverse, such as Destroy. + class RewindBase < Base + # Rewind action manifest. + def invoke! + manifest.rewind(self) + end + end + + + # Create is the premier generator command. It copies files, creates + # directories, renders templates, and more. + class Create < Base + + # Check whether the given class names are already taken by + # Ruby or Rails. In the future, expand to check other namespaces + # such as the rest of the user's app. + def class_collisions(*class_names) + path = class_names.shift + class_names.flatten.each do |class_name| + # Convert to string to allow symbol arguments. + class_name = class_name.to_s + + # Skip empty strings. + next if class_name.strip.empty? + + # Split the class from its module nesting. + nesting = class_name.split('::') + name = nesting.pop + + # Hack to limit const_defined? to non-inherited on 1.9. + extra = [] + extra << false unless Object.method(:const_defined?).arity == 1 + + # Extract the last Module in the nesting. + last = nesting.inject(Object) { |last, nest| + break unless last.const_defined?(nest, *extra) + last.const_get(nest) + } + + # If the last Module exists, check whether the given + # class exists and raise a collision if so. + if last and last.const_defined?(name.camelize, *extra) + raise_class_collision(class_name) + end + end + end + + # Copy a file from source to destination with collision checking. + # + # The file_options hash accepts :chmod and :shebang and :collision options. + # :chmod sets the permissions of the destination file: + # file 'config/empty.log', 'log/test.log', :chmod => 0664 + # :shebang sets the #!/usr/bin/ruby line for scripts + # file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby' + # :collision sets the collision option only for the destination file: + # file 'settings/server.yml', 'config/server.yml', :collision => :skip + # + # Collisions are handled by checking whether the destination file + # exists and either skipping the file, forcing overwrite, or asking + # the user what to do. + def file(relative_source, relative_destination, file_options = {}, &block) + # Determine full paths for source and destination files. + source = source_path(relative_source) + destination = destination_path(relative_destination) + destination_exists = File.exist?(destination) + + # If source and destination are identical then we're done. + if destination_exists and identical?(source, destination, &block) + return logger.identical(relative_destination) + end + + # Check for and resolve file collisions. + if destination_exists + + # Make a choice whether to overwrite the file. :force and + # :skip already have their mind made up, but give :ask a shot. + choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask + when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block) + when :force then :force + when :skip then :skip + else raise "Invalid collision option: #{options[:collision].inspect}" + end + + # Take action based on our choice. Bail out if we chose to + # skip the file; otherwise, log our transgression and continue. + case choice + when :force then logger.force(relative_destination) + when :skip then return(logger.skip(relative_destination)) + else raise "Invalid collision choice: #{choice}.inspect" + end + + # File doesn't exist so log its unbesmirched creation. + else + logger.create relative_destination + end + + # If we're pretending, back off now. + return if options[:pretend] + + # Write destination file with optional shebang. Yield for content + # if block given so templaters may render the source file. If a + # shebang is requested, replace the existing shebang or insert a + # new one. + File.open(destination, 'wb') do |dest| + dest.write render_file(source, file_options, &block) + end + + # Optionally change permissions. + if file_options[:chmod] + FileUtils.chmod(file_options[:chmod], destination) + end + + # Optionally add file to subversion or git + system("svn add #{destination}") if options[:svn] + system("git add -v #{relative_destination}") if options[:git] + end + + # Checks if the source and the destination file are identical. If + # passed a block then the source file is a template that needs to first + # be evaluated before being compared to the destination. + def identical?(source, destination, &block) + return false if File.directory? destination + source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source) + destination = IO.read(destination) + source == destination + end + + # Generate a file for a Rails application using an ERuby template. + # Looks up and evaluates a template by name and writes the result. + # + # The ERB template uses explicit trim mode to best control the + # proliferation of whitespace in generated code. <%- trims leading + # whitespace; -%> trims trailing whitespace including one newline. + # + # A hash of template options may be passed as the last argument. + # The options accepted by the file are accepted as well as :assigns, + # a hash of variable bindings. Example: + # template 'foo', 'bar', :assigns => { :action => 'view' } + # + # Template is implemented in terms of file. It calls file with a + # block which takes a file handle and returns its rendered contents. + def template(relative_source, relative_destination, template_options = {}) + file(relative_source, relative_destination, template_options) do |file| + # Evaluate any assignments in a temporary, throwaway binding. + vars = template_options[:assigns] || {} + b = template_options[:binding] || binding + vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b } + + # Render the source file with the temporary binding. + ERB.new(file.read, nil, '-').result(b) + end + end + + def complex_template(relative_source, relative_destination, template_options = {}) + options = template_options.dup + options[:assigns] ||= {} + options[:assigns]['template_for_inclusion'] = render_template_part(template_options) + template(relative_source, relative_destination, options) + end + + # Create a directory including any missing parent directories. + # Always skips directories which exist. + def directory(relative_path) + path = destination_path(relative_path) + if File.exist?(path) + logger.exists relative_path + else + logger.create relative_path + unless options[:pretend] + FileUtils.mkdir_p(path) + # git doesn't require adding the paths, adding the files later will + # automatically do a path add. + + # Subversion doesn't do path adds, so we need to add + # each directory individually. + # So stack up the directory tree and add the paths to + # subversion in order without recursion. + if options[:svn] + stack = [relative_path] + until File.dirname(stack.last) == stack.last # dirname('.') == '.' + stack.push File.dirname(stack.last) + end + stack.reverse_each do |rel_path| + svn_path = destination_path(rel_path) + system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn')) + end + end + end + end + end + + # Creates symlink + def symlink(relative_source, relative_destination) + path = destination_path(relative_destination) + if File.exist?(path) + logger.exists relative_destination + else + logger.create relative_destination + FileUtils.ln_s(relative_source, path) + end + end + + # Display a README. + def readme(*relative_sources) + relative_sources.flatten.each do |relative_source| + logger.readme relative_source + puts File.read(source_path(relative_source)) unless options[:pretend] + end + end + + # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template. + def migration_template(relative_source, relative_destination, template_options = {}) + migration_directory relative_destination + migration_file_name = template_options[:migration_file_name] || file_name + raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name) + template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options) + end + + def route_resources(*resources) + resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + logger.route "map.resources #{resource_list}" + unless options[:pretend] + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n map.resources #{resource_list}\n" + end + end + end + + private + def render_file(path, options = {}) + File.open(path, 'rb') do |file| + if block_given? + yield file + else + content = '' + if shebang = options[:shebang] + content << "#!#{shebang}\n" + if line = file.gets + content << "line\n" if line !~ /^#!/ + end + end + content << file.read + end + end + end + + # Raise a usage error with an informative WordNet suggestion. + # Thanks to Florian Gross (flgr). + def raise_class_collision(class_name) + message = <([\w ]*?)<\/a>/s).uniq + end + end + rescue Exception + return nil + end + end + + + # Undo the actions performed by a generator. Rewind the action + # manifest and attempt to completely erase the results of each action. + class Destroy < RewindBase + # Remove a file if it exists and is a file. + def file(relative_source, relative_destination, file_options = {}) + destination = destination_path(relative_destination) + if File.exist?(destination) + logger.rm relative_destination + unless options[:pretend] + if options[:svn] + # If the file has been marked to be added + # but has not yet been checked in, revert and delete + if options[:svn][relative_destination] + system("svn revert #{destination}") + FileUtils.rm(destination) + else + # If the directory is not in the status list, it + # has no modifications so we can simply remove it + system("svn rm #{destination}") + end + elsif options[:git] + if options[:git][:new][relative_destination] + # file has been added, but not committed + system("git reset HEAD #{relative_destination}") + FileUtils.rm(destination) + elsif options[:git][:modified][relative_destination] + # file is committed and modified + system("git rm -f #{relative_destination}") + else + # If the directory is not in the status list, it + # has no modifications so we can simply remove it + system("git rm #{relative_destination}") + end + else + FileUtils.rm(destination) + end + end + else + logger.missing relative_destination + return + end + end + + # Templates are deleted just like files and the actions take the + # same parameters, so simply alias the file method. + alias_method :template, :file + + # Remove each directory in the given path from right to left. + # Remove each subdirectory if it exists and is a directory. + def directory(relative_path) + parts = relative_path.split('/') + until parts.empty? + partial = File.join(parts) + path = destination_path(partial) + if File.exist?(path) + if Dir[File.join(path, '*')].empty? + logger.rmdir partial + unless options[:pretend] + if options[:svn] + # If the directory has been marked to be added + # but has not yet been checked in, revert and delete + if options[:svn][relative_path] + system("svn revert #{path}") + FileUtils.rmdir(path) + else + # If the directory is not in the status list, it + # has no modifications so we can simply remove it + system("svn rm #{path}") + end + # I don't think git needs to remove directories?.. + # or maybe they have special consideration... + else + FileUtils.rmdir(path) + end + end + else + logger.notempty partial + end + else + logger.missing partial + end + parts.pop + end + end + + def complex_template(*args) + # nothing should be done here + end + + # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}". + def migration_template(relative_source, relative_destination, template_options = {}) + migration_directory relative_destination + + migration_file_name = template_options[:migration_file_name] || file_name + unless migration_exists?(migration_file_name) + puts "There is no migration named #{migration_file_name}" + return + end + + + existing_migrations(migration_file_name).each do |file_path| + file(relative_source, file_path, template_options) + end + end + + def route_resources(*resources) + resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') + look_for = "\n map.resources #{resource_list}\n" + logger.route "map.resources #{resource_list}" + gsub_file 'config/routes.rb', /(#{look_for})/mi, '' + end + end + + + # List a generator's action manifest. + class List < Base + def dependency(generator_name, args, options = {}) + logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})" + end + + def class_collisions(*class_names) + logger.class_collisions class_names.join(', ') + end + + def file(relative_source, relative_destination, options = {}) + logger.file relative_destination + end + + def template(relative_source, relative_destination, options = {}) + logger.template relative_destination + end + + def complex_template(relative_source, relative_destination, options = {}) + logger.template "#{options[:insert]} inside #{relative_destination}" + end + + def directory(relative_path) + logger.directory "#{destination_path(relative_path)}/" + end + + def readme(*args) + logger.readme args.join(', ') + end + + def migration_template(relative_source, relative_destination, options = {}) + migration_directory relative_destination + logger.migration_template file_name + end + + def route_resources(*resources) + resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') + logger.route "map.resources #{resource_list}" + end + end + + # Update generator's action manifest. + class Update < Create + def file(relative_source, relative_destination, options = {}) + # logger.file relative_destination + end + + def template(relative_source, relative_destination, options = {}) + # logger.template relative_destination + end + + def complex_template(relative_source, relative_destination, template_options = {}) + + begin + dest_file = destination_path(relative_destination) + source_to_update = File.readlines(dest_file).join + rescue Errno::ENOENT + logger.missing relative_destination + return + end + + logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}" + + begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id])) + end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id])) + + # Refreshing inner part of the template with freshly rendered part. + rendered_part = render_template_part(template_options) + source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part) + + File.open(dest_file, 'w') { |file| file.write(source_to_update) } + end + + def directory(relative_path) + # logger.directory "#{destination_path(relative_path)}/" + end + end + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generated_attribute.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generated_attribute.rb new file mode 100644 index 00000000000..a3d4a01142d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generated_attribute.rb @@ -0,0 +1,46 @@ +require 'optparse' + +module Rails + module Generator + class GeneratedAttribute + attr_accessor :name, :type, :column + + def initialize(name, type) + @name, @type = name, type.to_sym + @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type) + end + + def field_type + @field_type ||= case type + when :integer, :float, :decimal then :text_field + when :datetime, :timestamp, :time then :datetime_select + when :date then :date_select + when :string then :text_field + when :text then :text_area + when :boolean then :check_box + else + :text_field + end + end + + def default + @default ||= case type + when :integer then 1 + when :float then 1.5 + when :decimal then "9.99" + when :datetime, :timestamp, :time then Time.now.to_s(:db) + when :date then Date.today.to_s(:db) + when :string then "MyString" + when :text then "MyText" + when :boolean then false + else + "" + end + end + + def reference? + [ :references, :belongs_to ].include?(self.type) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/USAGE new file mode 100644 index 00000000000..36d6061a599 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/USAGE @@ -0,0 +1,9 @@ +Description: + The 'rails' command creates a new Rails application with a default + directory structure and configuration at the path you specify. + +Example: + rails ~/Code/Ruby/weblog + + This generates a skeletal Rails installation in ~/Code/Ruby/weblog. + See the README in the newly created application to get going. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/app_generator.rb new file mode 100644 index 00000000000..9bc461be69f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -0,0 +1,270 @@ +require 'rbconfig' +require File.dirname(__FILE__) + '/template_runner' +require 'digest/md5' +require 'active_support/secure_random' + +class AppGenerator < Rails::Generator::Base + DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + + DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) + DEFAULT_DATABASE = 'sqlite3' + + mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.." + default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE), + :shebang => DEFAULT_SHEBANG, :with_dispatchers => false, :freeze => false + + + def initialize(runtime_args, runtime_options = {}) + super + + usage if args.empty? + usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db])) + + @destination_root = args.shift + @app_name = File.basename(File.expand_path(@destination_root)) + end + + def manifest + record do |m| + create_directories(m) + create_root_files(m) + create_app_files(m) + create_config_files(m) + create_script_files(m) + create_test_files(m) + create_public_files(m) + create_documentation_file(m) + create_log_files(m) + + create_debian_railties_link(m) + end + end + + def after_generate + if options[:template] + Rails::TemplateRunner.new(options[:template], @destination_root) + end + end + + protected + def banner + "Usage: #{$0} /path/to/your/app [options]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("-r", "--ruby=path", String, + "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).", + "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v } + + opt.on("-d", "--database=name", String, + "Preconfigure for selected database (options: #{DATABASES.join('/')}).", + "Default: #{DEFAULT_DATABASE}") { |v| options[:db] = v } + + opt.on("-D", "--with-dispatchers", + "Add CGI/FastCGI/mod_ruby dispatches code to generated application skeleton", + "Default: false") { |v| options[:with_dispatchers] = v } + + opt.on("-f", "--freeze", + "Freeze Rails in vendor/rails from the gems generating the skeleton", + "Default: false") { |v| options[:freeze] = v } + + opt.on("-m", "--template=path", String, + "Use an application template that lives at path (can be a filesystem path or URL).", + "Default: (none)") { |v| options[:template] = v } + + end + + + private + def create_directories(m) + m.directory '' + + # Intermediate directories are automatically created so don't sweat their absence here. + %w( + app/controllers + app/helpers + app/models + app/views/layouts + config/environments + config/initializers + config/locales + db + doc + lib + lib/tasks + log + public/images + public/javascripts + public/stylesheets + script/performance + test/fixtures + test/functional + test/integration + test/performance + test/unit + vendor + vendor/plugins + tmp/sessions + tmp/sockets + tmp/cache + tmp/pids + ).each { |path| m.directory(path) } + end + + def create_root_files(m) + m.file "fresh_rakefile", "Rakefile" + m.file "README", "README" + end + + def create_app_files(m) + m.file "helpers/application_controller.rb", "app/controllers/application_controller.rb" + m.file "helpers/application_helper.rb", "app/helpers/application_helper.rb" + end + + def create_config_files(m) + create_database_configuration_file(m) + create_routes_file(m) + create_locale_file(m) + create_seeds_file(m) + create_initializer_files(m) + create_environment_files(m) + end + + def create_documentation_file(m) + m.file "doc/README_FOR_APP", "doc/README_FOR_APP" + end + + def create_log_files(m) + %w( server production development test ).each do |file| + m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 + end + end + + def create_public_files(m) + create_dispatch_files(m) + create_error_files(m) + create_welcome_file(m) + create_browser_convention_files(m) + create_rails_image(m) + create_javascript_files(m) + end + + def create_script_files(m) + %w( + about console dbconsole destroy generate runner server plugin + performance/benchmarker performance/profiler + ).each do |file| + m.file "bin/#{file}", "script/#{file}", { + :chmod => 0755, + :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] + } + end + end + + def create_test_files(m) + m.file "helpers/test_helper.rb", "test/test_helper.rb" + m.file "helpers/performance_test.rb", "test/performance/browsing_test.rb" + end + + + def create_database_configuration_file(m) + m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { + :app_name => @app_name, + :socket => options[:db] == "mysql" ? mysql_socket_location : nil } + end + + def create_routes_file(m) + m.file "configs/routes.rb", "config/routes.rb" + end + + def create_seeds_file(m) + m.file "configs/seeds.rb", "db/seeds.rb" + end + + def create_initializer_files(m) + %w( + backtrace_silencers + inflections + mime_types + new_rails_defaults + ).each do |initializer| + m.file "configs/initializers/#{initializer}.rb", "config/initializers/#{initializer}.rb" + end + + m.template "configs/initializers/session_store.rb", "config/initializers/session_store.rb", + :assigns => { :app_name => @app_name, :app_secret => ActiveSupport::SecureRandom.hex(64) } + end + + def create_locale_file(m) + m.file "configs/locales/en.yml", "config/locales/en.yml" + end + + def create_environment_files(m) + m.template "environments/environment.rb", "config/environment.rb", + :assigns => { :freeze => options[:freeze] } + + m.file "environments/boot.rb", "config/boot.rb" + m.file "environments/production.rb", "config/environments/production.rb" + m.file "environments/development.rb", "config/environments/development.rb" + m.file "environments/test.rb", "config/environments/test.rb" + end + + + def create_dispatch_files(m) + if options[:with_dispatchers] + dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } + + m.file "dispatches/config.ru", "config.ru" + m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options + m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options + m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options + end + end + + def create_error_files(m) + %w( 404 422 500 ).each do |file| + m.file "html/#{file}.html", "public/#{file}.html" + end + end + + def create_welcome_file(m) + m.file 'html/index.html', 'public/index.html' + end + + def create_browser_convention_files(m) + m.file "html/favicon.ico", "public/favicon.ico" + m.file "html/robots.txt", "public/robots.txt" + end + + def create_rails_image(m) + m.file "html/images/rails.png", "public/images/rails.png" + end + + def create_javascript_files(m) + %w( prototype effects dragdrop controls application ).each do |javascript| + m.file "html/javascripts/#{javascript}.js", "public/javascripts/#{javascript}.js" + end + end + + def create_debian_railties_link(m) + m.symlink "/usr/share/rails-ruby1.8", "vendor/rails" + m.symlink "/usr/share/rails-ruby1.8/railties", "vendor/railties" + end + + + def mysql_socket_location + [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/scm/git.rb new file mode 100644 index 00000000000..a53494de507 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/scm/git.rb @@ -0,0 +1,18 @@ +STDOUT.sync = true + +module Rails + class Git < Scm + def self.clone(repos, branch=nil) + system "git clone #{repos}" + + if branch + system "cd #{repos.split('/').last}/" + system "git checkout #{branch}" + end + end + + def self.run(command) + system "git #{command}" + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/scm/scm.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/scm/scm.rb new file mode 100644 index 00000000000..f6c08cad39c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/scm/scm.rb @@ -0,0 +1,8 @@ +module Rails + class Scm + private + def self.hash_to_parameters(hash) + hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ") + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/template_runner.rb new file mode 100644 index 00000000000..40f21cc457c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/applications/app/template_runner.rb @@ -0,0 +1,401 @@ +require File.dirname(__FILE__) + '/scm/scm' +require File.dirname(__FILE__) + '/scm/git' +require File.dirname(__FILE__) + '/scm/svn' + +require 'open-uri' +require 'fileutils' + +module Rails + class TemplateRunner + attr_reader :root + attr_writer :logger + + def initialize(template, root = '') # :nodoc: + @root = File.expand_path(File.directory?(root) ? root : File.join(Dir.pwd, root)) + + log 'applying', "template: #{template}" + + load_template(template) + + log 'applied', "#{template}" + end + + def load_template(template) + begin + code = open(template).read + in_root { self.instance_eval(code) } + rescue LoadError, Errno::ENOENT => e + raise "The template [#{template}] could not be loaded. Error: #{e}" + end + end + + # Create a new file in the Rails project folder. Specify the + # relative path from RAILS_ROOT. Data is the return value of a block + # or a data string. + # + # ==== Examples + # + # file("lib/fun_party.rb") do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # file("config/apach.conf", "your apache config") + # + def file(filename, data = nil, log_action = true, &block) + log 'file', filename if log_action + dir, file = [File.dirname(filename), File.basename(filename)] + + inside(dir) do + File.open(file, "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Install a plugin. You must provide either a Subversion url or Git url. + # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned. + # + # ==== Examples + # + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true + # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + # + def plugin(name, options) + log 'plugin', name + + if options[:git] && options[:submodule] + in_root do + Git.run("submodule add #{options[:git]} vendor/plugins/#{name}") + end + elsif options[:git] || options[:svn] + in_root do + run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false) + end + else + log "! no git or svn provided for #{name}. skipping..." + end + end + + # Adds an entry into config/environment.rb for the supplied gem : + def gem(name, options = {}) + log 'gem', name + env = options.delete(:env) + + gems_code = "config.gem '#{name}'" + + if options.any? + opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ") + gems_code << ", #{opts}" + end + + environment gems_code, :env => env + end + + # Adds a line inside the Initializer block for config/environment.rb. Used by #gem + # If options :env is specified, the line is appended to the corresponding + # file in config/environments/#{env}.rb + def environment(data = nil, options = {}, &block) + sentinel = 'Rails::Initializer.run do |config|' + + data = block.call if !data && block_given? + + in_root do + if options[:env].nil? + gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n " << data + end + else + Array.wrap(options[:env]).each do|env| + append_file "config/environments/#{env}.rb", "\n#{data}" + end + end + end + end + + # Run a command in git. + # + # ==== Examples + # + # git :init + # git :add => "this.file that.rb" + # git :add => "onefile.rb", :rm => "badfile.cxx" + # + def git(command = {}) + in_root do + if command.is_a?(Symbol) + log 'running', "git #{command}" + Git.run(command.to_s) + else + command.each do |command, options| + log 'running', "git #{command} #{options}" + Git.run("#{command} #{options}") + end + end + end + end + + # Create a new file in the vendor/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # vendor("sekrit.rb") do + # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" + # "salt = '#{sekrit_salt}'" + # end + # + # vendor("foreign.rb", "# Foreign code is fun") + # + def vendor(filename, data = nil, &block) + log 'vendoring', filename + file("vendor/#{filename}", data, false, &block) + end + + # Create a new file in the lib/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # lib("crypto.rb") do + # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" + # end + # + # lib("foreign.rb", "# Foreign code is fun") + # + def lib(filename, data = nil, &block) + log 'lib', filename + file("lib/#{filename}", data, false, &block) + end + + # Create a new Rakefile with the provided code (either in a block or a string). + # + # ==== Examples + # + # rakefile("bootstrap.rake") do + # project = ask("What is the UNIX name of your project?") + # + # <<-TASK + # namespace :#{project} do + # task :bootstrap do + # puts "i like boots!" + # end + # end + # TASK + # end + # + # rakefile("seed.rake", "puts 'im plantin ur seedz'") + # + def rakefile(filename, data = nil, &block) + log 'rakefile', filename + file("lib/tasks/#{filename}", data, false, &block) + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # ==== Examples + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do + # data << "#{const} = :entp" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + # + def initializer(filename, data = nil, &block) + log 'initializer', filename + file("config/initializers/#{filename}", data, false, &block) + end + + # Generate something using a generator from Rails or a plugin. + # The second parameter is the argument string that is passed to + # the generator or an Array that is joined. + # + # ==== Example + # + # generate(:authenticated, "user session") + # + def generate(what, *args) + log 'generating', what + argument = args.map(&:to_s).flatten.join(" ") + + in_root { run_ruby_script("script/generate #{what} #{argument}", false) } + end + + # Executes a command + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + def run(command, log_action = true) + log 'executing', "#{command} from #{Dir.pwd}" if log_action + `#{command}` + end + + # Executes a ruby script (taking into account WIN32 platform quirks) + def run_ruby_script(command, log_action = true) + ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : '' + run("#{ruby_command}#{command}", log_action) + end + + # Runs the supplied rake task + # + # ==== Example + # + # rake("db:migrate") + # rake("db:migrate", :env => "production") + # rake("gems:install", :sudo => true) + # + def rake(command, options = {}) + log 'rake', command + env = options[:env] || 'development' + sudo = options[:sudo] ? 'sudo ' : '' + in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) } + end + + # Just run the capify command in root + # + # ==== Example + # + # capify! + # + def capify! + log 'capifying' + in_root { run('capify .', false) } + end + + # Add Rails to /vendor/rails + # + # ==== Example + # + # freeze! + # + def freeze!(args = {}) + log 'vendor', 'rails edge' + in_root { run('rake rails:freeze:edge', false) } + end + + # Make an entry in Rails routing file conifg/routes.rb + # + # === Example + # + # route "map.root :controller => :welcome" + # + def route(routing_code) + log 'route', routing_code + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + in_root do + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{routing_code}\n" + end + end + end + + protected + + # Get a user's input + # + # ==== Example + # + # answer = ask("Should I freeze the latest Rails?") + # freeze! if ask("Should I freeze the latest Rails?") == "yes" + # + def ask(string) + log '', string + STDIN.gets.strip + end + + # Do something in the root of the Rails application or + # a provided subfolder; the full path is yielded to the block you provide. + # The path is set back to the previous path when the method exits. + def inside(dir = '', &block) + folder = File.join(root, dir) + FileUtils.mkdir_p(folder) unless File.exist?(folder) + FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } + end + + def in_root + FileUtils.cd(root) { yield } + end + + # Helper to test if the user says yes(y)? + # + # ==== Example + # + # freeze! if yes?("Should I freeze the latest Rails?") + # + def yes?(question) + answer = ask(question).downcase + answer == "y" || answer == "yes" + end + + # Helper to test if the user does NOT say yes(y)? + # + # ==== Example + # + # capify! if no?("Will you be using vlad to deploy your application?") + # + def no?(question) + !yes?(question) + end + + # Run a regular expression replacement on a file + # + # ==== Example + # + # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + def gsub_file(relative_destination, regexp, *args, &block) + path = destination_path(relative_destination) + content = File.read(path).gsub(regexp, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + + # Append text to a file + # + # ==== Example + # + # append_file 'config/environments/test.rb', 'config.gem "rspec"' + # + def append_file(relative_destination, data) + path = destination_path(relative_destination) + File.open(path, 'ab') { |file| file.write(data) } + end + + def destination_path(relative_destination) + File.join(root, relative_destination) + end + + def log(action, message = '') + logger.log(action, message) + end + + def logger + @logger ||= Rails::Generator::Base.logger + end + + def logger + @logger ||= if defined?(Rails::Generator::Base) + Rails::Generator::Base.logger + else + require 'rails_generator/simple_logger' + Rails::Generator::SimpleLogger.new(STDOUT) + end + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/USAGE new file mode 100644 index 00000000000..362872e84ab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/USAGE @@ -0,0 +1,30 @@ +Description: + Stubs out a new controller and its views. Pass the controller name, either + CamelCased or under_scored, and a list of views as arguments. + + To create a controller within a module, specify the controller name as a + path like 'parent_module/controller_name'. + + This generates a controller class in app/controllers, view templates in + app/views/controller_name, a helper class in app/helpers, a functional + test suite in test/functional and a helper test suite in test/unit/helpers. + +Example: + `./script/generate controller CreditCard open debit credit close` + + Credit card controller with URLs like /credit_card/debit. + Controller: app/controllers/credit_card_controller.rb + Functional Test: test/functional/credit_card_controller_test.rb + Views: app/views/credit_card/debit.html.erb [...] + Helper: app/helpers/credit_card_helper.rb + Helper Test: test/unit/helpers/credit_card_helper_test.rb + +Modules Example: + `./script/generate controller 'admin/credit_card' suspend late_fee` + + Credit card admin controller with URLs /admin/credit_card/suspend. + Controller: app/controllers/admin/credit_card_controller.rb + Functional Test: test/functional/admin/credit_card_controller_test.rb + Views: app/views/admin/credit_card/debit.html.erb [...] + Helper: app/helpers/admin/credit_card_helper.rb + Helper Test: test/unit/helpers/admin/credit_card_helper_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/controller_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/controller_generator.rb new file mode 100644 index 00000000000..dc126e8a989 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/controller_generator.rb @@ -0,0 +1,43 @@ +class ControllerGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper", "#{class_name}HelperTest" + + # Controller, helper, views, and test directories. + m.directory File.join('app/controllers', class_path) + m.directory File.join('app/helpers', class_path) + m.directory File.join('app/views', class_path, file_name) + m.directory File.join('test/functional', class_path) + m.directory File.join('test/unit/helpers', class_path) + + # Controller class, functional test, and helper class. + m.template 'controller.rb', + File.join('app/controllers', + class_path, + "#{file_name}_controller.rb") + + m.template 'functional_test.rb', + File.join('test/functional', + class_path, + "#{file_name}_controller_test.rb") + + m.template 'helper.rb', + File.join('app/helpers', + class_path, + "#{file_name}_helper.rb") + + m.template 'helper_test.rb', + File.join('test/unit/helpers', + class_path, + "#{file_name}_helper_test.rb") + + # View template for each action. + actions.each do |action| + path = File.join('app/views', class_path, file_name, "#{action}.html.erb") + m.template 'view.html.erb', path, + :assigns => { :action => action, :path => path } + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/controller.rb new file mode 100644 index 00000000000..cda2659e693 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/controller.rb @@ -0,0 +1,7 @@ +class <%= class_name %>Controller < ApplicationController +<% for action in actions -%> + def <%= action %> + end + +<% end -%> +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb new file mode 100644 index 00000000000..62fa5d86fdd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>ControllerTest < ActionController::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/helper.rb new file mode 100644 index 00000000000..3fe2ecdc74c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb new file mode 100644 index 00000000000..591e40900e9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb new file mode 100644 index 00000000000..ad85431f986 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb @@ -0,0 +1,2 @@ +

        <%= class_name %>#<%= action %>

        +

        Find me in <%= path %>

        diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/USAGE new file mode 100644 index 00000000000..ef27ca617e5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/USAGE @@ -0,0 +1,24 @@ +Description: + Stubs out a new helper. Pass the helper name, either + CamelCased or under_scored. + + To create a helper within a module, specify the helper name as a + path like 'parent_module/helper_name'. + + This generates a helper class in app/helpers and a helper test + suite in test/unit/helpers. + +Example: + `./script/generate helper CreditCard` + + Credit card helper. + Helper: app/helpers/credit_card_helper.rb + Test: test/unit/helpers/credit_card_helper_test.rb + +Modules Example: + `./script/generate helper 'admin/credit_card'` + + Credit card admin helper. + Helper: app/helpers/admin/credit_card_helper.rb + Test: test/unit/helpers/admin/credit_card_helper_test.rb + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/helper_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/helper_generator.rb new file mode 100644 index 00000000000..f7831f7c7ab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/helper_generator.rb @@ -0,0 +1,25 @@ +class HelperGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_path, "#{class_name}Helper", "#{class_name}HelperTest" + + # Helper and helper test directories. + m.directory File.join('app/helpers', class_path) + m.directory File.join('test/unit/helpers', class_path) + + # Helper and helper test class. + + m.template 'helper.rb', + File.join('app/helpers', + class_path, + "#{file_name}_helper.rb") + + m.template 'helper_test.rb', + File.join('test/unit/helpers', + class_path, + "#{file_name}_helper_test.rb") + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/templates/helper.rb new file mode 100644 index 00000000000..3fe2ecdc74c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb new file mode 100644 index 00000000000..591e40900e9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/USAGE new file mode 100644 index 00000000000..09e2691f690 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/USAGE @@ -0,0 +1,8 @@ +Description: + Stubs out a new integration test. Pass the name of the test, either + CamelCased or under_scored, as an argument. The new test class is + generated in test/integration/testname_test.rb + +Example: + `./script/generate integration_test GeneralStories` creates a GeneralStories + integration test in test/integration/general_stories_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb new file mode 100644 index 00000000000..44323f28cac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb @@ -0,0 +1,16 @@ +class IntegrationTestGenerator < Rails::Generator::NamedBase + default_options :skip_migration => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # integration test directory + m.directory File.join('test/integration', class_path) + + # integration test stub + m.template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb new file mode 100644 index 00000000000..2c57158b1c4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb @@ -0,0 +1,10 @@ +require 'test_helper' + +class <%= class_name %>Test < ActionController::IntegrationTest + fixtures :all + + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/USAGE new file mode 100644 index 00000000000..61a649ed4d2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/USAGE @@ -0,0 +1,16 @@ +Description: + Stubs out a new mailer and its views. Pass the mailer name, either + CamelCased or under_scored, and an optional list of emails as arguments. + + This generates a mailer class in app/models, view templates in + app/views/mailer_name, a unit test in test/unit, and fixtures in + test/fixtures. + +Example: + `./script/generate mailer Notifications signup forgot_password invoice` + + creates a Notifications mailer class, views, test, and fixtures: + Mailer: app/models/notifications.rb + Views: app/views/notifications/signup.erb [...] + Test: test/unit/test/unit/notifications_test.rb + Fixtures: test/fixtures/notifications/signup [...] diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb new file mode 100644 index 00000000000..ba6d60cac61 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb @@ -0,0 +1,30 @@ +class MailerGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # Mailer, view, test, and fixture directories. + m.directory File.join('app/models', class_path) + m.directory File.join('app/views', file_path) + m.directory File.join('test/unit', class_path) + m.directory File.join('test/fixtures', file_path) + + # Mailer class and unit test. + m.template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb") + m.template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb") + + # View template and fixture for each action. + actions.each do |action| + relative_path = File.join(file_path, action) + view_path = File.join('app/views', "#{relative_path}.erb") + fixture_path = File.join('test/fixtures', relative_path) + + m.template "view.erb", view_path, + :assigns => { :action => action, :path => view_path } + m.template "fixture.erb", fixture_path, + :assigns => { :action => action, :path => view_path } + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb new file mode 100644 index 00000000000..6899257ddcb --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb @@ -0,0 +1,3 @@ +<%= class_name %>#<%= action %> + +Find me in <%= path %> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb new file mode 100644 index 00000000000..ce15ae9de90 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb @@ -0,0 +1,15 @@ +class <%= class_name %> < ActionMailer::Base + +<% for action in actions -%> + + def <%= action %>(sent_at = Time.now) + subject '<%= class_name %>#<%= action %>' + recipients '' + from '' + sent_on sent_at + + body :greeting => 'Hi,' + end +<% end -%> + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb new file mode 100644 index 00000000000..4de94076e97 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class <%= class_name %>Test < ActionMailer::TestCase +<% for action in actions -%> + test "<%= action %>" do + @expected.subject = '<%= class_name %>#<%= action %>' + @expected.body = read_fixture('<%= action %>') + @expected.date = Time.now + + assert_equal @expected.encoded, <%= class_name %>.create_<%= action %>(@expected.date).encoded + end + +<% end -%> +<% if actions.blank? -%> + # replace this with your real tests + test "the truth" do + assert true + end +<% end -%> +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/view.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/view.erb new file mode 100644 index 00000000000..6899257ddcb --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/view.erb @@ -0,0 +1,3 @@ +<%= class_name %>#<%= action %> + +Find me in <%= path %> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/USAGE new file mode 100644 index 00000000000..123ec6c03f3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/USAGE @@ -0,0 +1,8 @@ +Description: + Cast some metal! + +Examples: + `./script/generate metal poller` + + This will create: + Metal: app/metal/poller.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/metal_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/metal_generator.rb new file mode 100644 index 00000000000..64f49d929d8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/metal_generator.rb @@ -0,0 +1,8 @@ +class MetalGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + m.directory 'app/metal' + m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/templates/metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/templates/metal.rb new file mode 100644 index 00000000000..e94982b69ac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/metal/templates/metal.rb @@ -0,0 +1,12 @@ +# Allow the metal piece to run in isolation +require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails) + +class <%= class_name %> + def self.call(env) + if env["PATH_INFO"] =~ /^\/<%= file_name %>/ + [200, {"Content-Type" => "text/html"}, ["Hello, World!"]] + else + [404, {"Content-Type" => "text/html"}, ["Not Found"]] + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/USAGE new file mode 100644 index 00000000000..b83c657963b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/USAGE @@ -0,0 +1,29 @@ +Description: + Stubs out a new database migration. Pass the migration name, either + CamelCased or under_scored, and an optional list of attribute pairs as arguments. + + A migration class is generated in db/migrate prefixed by a timestamp of the current date and time. + + You can name your migration in either of these formats to generate add/remove + column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable + +Example: + `./script/generate migration AddSslFlag` + + If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration + db/migrate/20080514090912_add_ssl_flag.rb + + `./script/generate migration AddTitleBodyToPost title:string body:text published:boolean` + + This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with + this in the Up migration: + + add_column :posts, :title, :string + add_column :posts, :body, :text + add_column :posts, :published, :boolean + + And this in the Down migration: + + remove_column :posts, :published + remove_column :posts, :body + remove_column :posts, :title diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/migration_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/migration_generator.rb new file mode 100644 index 00000000000..acf41e07df5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/migration_generator.rb @@ -0,0 +1,20 @@ +class MigrationGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + m.migration_template 'migration.rb', 'db/migrate', :assigns => get_local_assigns + end + end + + + private + def get_local_assigns + returning(assigns = {}) do + if class_name.underscore =~ /^(add|remove)_.*_(?:to|from)_(.*)/ + assigns[:migration_action] = $1 + assigns[:table_name] = $2.pluralize + else + assigns[:attributes] = [] + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/templates/migration.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/templates/migration.rb new file mode 100644 index 00000000000..ca35a432294 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/migration/templates/migration.rb @@ -0,0 +1,11 @@ +class <%= class_name.underscore.camelize %> < ActiveRecord::Migration + def self.up<% attributes.each do |attribute| %> + <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%> + <%- end %> + end + + def self.down<% attributes.reverse.each do |attribute| %> + <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%> + <%- end %> + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/USAGE new file mode 100644 index 00000000000..24b03b4d4aa --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/USAGE @@ -0,0 +1,27 @@ +Description: + Stubs out a new model. Pass the model name, either CamelCased or + under_scored, and an optional list of attribute pairs as arguments. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + You don't have to think up every attribute up front, but it helps to + sketch out a few so you can start working with the model immediately. + + This generates a model class in app/models, a unit test in test/unit, + a test fixture in test/fixtures/singular_name.yml, and a migration in + db/migrate. + +Examples: + `./script/generate model account` + + creates an Account model, test, fixture, and migration: + Model: app/models/account.rb + Test: test/unit/account_test.rb + Fixtures: test/fixtures/accounts.yml + Migration: db/migrate/XXX_add_accounts.rb + + `./script/generate model post title:string body:text published:boolean` + + creates a Post model with a string title, text body, and published flag. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/model_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/model_generator.rb new file mode 100644 index 00000000000..1895d8a9c6e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/model_generator.rb @@ -0,0 +1,52 @@ +class ModelGenerator < Rails::Generator::NamedBase + default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # Model, test, and fixture directories. + m.directory File.join('app/models', class_path) + m.directory File.join('test/unit', class_path) + m.directory File.join('test/fixtures', class_path) + + # Model class, unit test, and fixtures. + m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb") + m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb") + + unless options[:skip_fixture] + m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") + end + + migration_file_path = file_path.gsub(/\//, '_') + migration_name = class_name + if ActiveRecord::Base.pluralize_table_names + migration_name = migration_name.pluralize + migration_file_path = migration_file_path.pluralize + end + + unless options[:skip_migration] + m.migration_template 'migration.rb', 'db/migrate', :assigns => { + :migration_name => "Create#{migration_name.gsub(/::/, '')}" + }, :migration_file_name => "create_#{migration_file_path}" + end + end + end + + protected + def banner + "Usage: #{$0} #{spec.name} ModelName [field:type, field:type]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-timestamps", + "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + opt.on("--skip-fixture", + "Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v} + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml new file mode 100644 index 00000000000..c21035113e5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml @@ -0,0 +1,19 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html + +<% unless attributes.empty? -%> +one: +<% for attribute in attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<% end -%> + +two: +<% for attribute in attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<% end -%> +<% else -%> +# one: +# column: value +# +# two: +# column: value +<% end -%> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/migration.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/migration.rb new file mode 100644 index 00000000000..382fd1156ee --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/migration.rb @@ -0,0 +1,16 @@ +class <%= migration_name %> < ActiveRecord::Migration + def self.up + create_table :<%= table_name %> do |t| +<% for attribute in attributes -%> + t.<%= attribute.type %> :<%= attribute.name %> +<% end -%> +<% unless options[:skip_timestamps] %> + t.timestamps +<% end -%> + end + end + + def self.down + drop_table :<%= table_name %> + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/model.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/model.rb new file mode 100644 index 00000000000..6fcf393bdf1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/model.rb @@ -0,0 +1,5 @@ +class <%= class_name %> < ActiveRecord::Base +<% attributes.select(&:reference?).each do |attribute| -%> + belongs_to :<%= attribute.name %> +<% end -%> +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb new file mode 100644 index 00000000000..3e0bc29d3ad --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/USAGE new file mode 100644 index 00000000000..a5d744a3c2a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/USAGE @@ -0,0 +1,13 @@ +Description: + Stubs out a new observer. Pass the observer name, either CamelCased or + under_scored, as an argument. + + The generator creates an observer class in app/models and a unit test in + test/unit. + +Example: + `./script/generate observer Account` + + creates an Account observer and unit test: + Observer: app/models/account_observer.rb + Test: test/unit/account_observer_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/observer_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/observer_generator.rb new file mode 100644 index 00000000000..3c4b330a801 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/observer_generator.rb @@ -0,0 +1,16 @@ +class ObserverGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions "#{class_name}Observer", "#{class_name}ObserverTest" + + # Observer, and test directories. + m.directory File.join('app/models', class_path) + m.directory File.join('test/unit', class_path) + + # Observer class and unit test fixtures. + m.template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb") + m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/observer.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/observer.rb new file mode 100644 index 00000000000..b9a3004161a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/observer.rb @@ -0,0 +1,2 @@ +class <%= class_name %>Observer < ActiveRecord::Observer +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb new file mode 100644 index 00000000000..03f6d5666e5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>ObserverTest < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/USAGE new file mode 100644 index 00000000000..d84051eb02d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/USAGE @@ -0,0 +1,8 @@ +Description: + Stubs out a new performance test. Pass the name of the test, either + CamelCased or under_scored, as an argument. The new test class is + generated in test/performance/testname_test.rb + +Example: + `./script/generate performance_test GeneralStories` creates a GeneralStories + performance test in test/performance/general_stories_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb new file mode 100644 index 00000000000..83ce8ac6740 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb @@ -0,0 +1,16 @@ +class PerformanceTestGenerator < Rails::Generator::NamedBase + default_options :skip_migration => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # performance test directory + m.directory File.join('test/performance', class_path) + + # performance test stub + m.template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb new file mode 100644 index 00000000000..27c91b0fcac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' +require 'performance_test_help' + +class <%= class_name %>Test < ActionController::PerformanceTest + # Replace this with your real tests. + def test_homepage + get '/' + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/USAGE new file mode 100644 index 00000000000..d2ecfc2d59c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/USAGE @@ -0,0 +1,25 @@ +Description: + Stubs out a new plugin. Pass the plugin name, either CamelCased or + under_scored, as an argument. Pass --with-generator to add an example + generator also. + + This creates a plugin in vendor/plugins including an init.rb and README + as well as standard lib, task, and test directories. + +Example: + `./script/generate plugin BrowserFilters` + + creates a standard browser_filters plugin: + vendor/plugins/browser_filters/README + vendor/plugins/browser_filters/init.rb + vendor/plugins/browser_filters/install.rb + vendor/plugins/browser_filters/lib/browser_filters.rb + vendor/plugins/browser_filters/test/browser_filters_test.rb + vendor/plugins/browser_filters/tasks/browser_filters_tasks.rake + + ./script/generate plugin BrowserFilters --with-generator + + creates a browser_filters generator also: + vendor/plugins/browser_filters/generators/browser_filters/browser_filters_generator.rb + vendor/plugins/browser_filters/generators/browser_filters/USAGE + vendor/plugins/browser_filters/generators/browser_filters/templates/ diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb new file mode 100644 index 00000000000..6826998252f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb @@ -0,0 +1,39 @@ +class PluginGenerator < Rails::Generator::NamedBase + attr_reader :plugin_path + + def initialize(runtime_args, runtime_options = {}) + @with_generator = runtime_args.delete("--with-generator") + super + @plugin_path = "vendor/plugins/#{file_name}" + end + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name + + m.directory "#{plugin_path}/lib" + m.directory "#{plugin_path}/tasks" + m.directory "#{plugin_path}/test" + + m.template 'README', "#{plugin_path}/README" + m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE" + m.template 'Rakefile', "#{plugin_path}/Rakefile" + m.template 'init.rb', "#{plugin_path}/init.rb" + m.template 'install.rb', "#{plugin_path}/install.rb" + m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb" + m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb" + m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake" + m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb" + m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb" + if @with_generator + m.directory "#{plugin_path}/generators" + m.directory "#{plugin_path}/generators/#{file_name}" + m.directory "#{plugin_path}/generators/#{file_name}/templates" + + m.template 'generator.rb', "#{plugin_path}/generators/#{file_name}/#{file_name}_generator.rb" + m.template 'USAGE', "#{plugin_path}/generators/#{file_name}/USAGE" + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE new file mode 100644 index 00000000000..8717df053d3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) <%= Date.today.year %> [name of plugin creator] + +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. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/README b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/README new file mode 100644 index 00000000000..702db07cb1e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/README @@ -0,0 +1,13 @@ +<%= class_name %> +<%= "=" * class_name.size %> + +Introduction goes here. + + +Example +======= + +Example goes here. + + +Copyright (c) <%= Date.today.year %> [name of plugin creator], released under the MIT license diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile new file mode 100644 index 00000000000..85e8ff18343 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile @@ -0,0 +1,23 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the <%= file_name %> plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +desc 'Generate documentation for the <%= file_name %> plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = '<%= class_name %>' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/USAGE new file mode 100644 index 00000000000..ea9f4f12cc6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/USAGE @@ -0,0 +1,8 @@ +Description: + Explain the generator + +Example: + ./script/generate <%= file_name %> Thing + + This will create: + what/will/it/create diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb new file mode 100644 index 00000000000..3e800df6c5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb @@ -0,0 +1,8 @@ +class <%= class_name %>Generator < Rails::Generator::NamedBase + def manifest + record do |m| + # m.directory "lib" + # m.template 'README', "README" + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/init.rb new file mode 100644 index 00000000000..3c19a743c9d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/init.rb @@ -0,0 +1 @@ +# Include hook code here diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/install.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/install.rb new file mode 100644 index 00000000000..f7732d3796c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/install.rb @@ -0,0 +1 @@ +# Install hook code here diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb new file mode 100644 index 00000000000..d8d908a9591 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb @@ -0,0 +1 @@ +# <%= class_name %> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake new file mode 100644 index 00000000000..72920a9d3a3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :<%= file_name %> do +# # Task goes here +# end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb new file mode 100644 index 00000000000..cf148b8b474 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb @@ -0,0 +1,3 @@ +require 'rubygems' +require 'active_support' +require 'active_support/test_case' \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb new file mode 100644 index 00000000000..97383334634 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb @@ -0,0 +1 @@ +# Uninstall hook code here diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb new file mode 100644 index 00000000000..3e0bc29d3ad --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/USAGE new file mode 100644 index 00000000000..e6043f1de19 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/USAGE @@ -0,0 +1,23 @@ +Description: + Stubs out a new resource including an empty model and controller suitable + for a restful, resource-oriented application. Pass the singular model name, + either CamelCased or under_scored, as the first argument, and an optional + list of attribute pairs. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + You don't have to think up every attribute up front, but it helps to + sketch out a few so you can start working with the resource immediately. + + This creates a model, controller, helper, tests and fixtures for all of them, + and the corresponding map.resources declaration in config/routes.rb + + Unlike the scaffold generator, the resource generator does not create + views or add any methods to the generated controller. + +Examples: + `./script/generate resource post` # no attributes + `./script/generate resource post title:string body:text published:boolean` + `./script/generate resource purchase order_id:integer amount:decimal` diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/resource_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/resource_generator.rb new file mode 100644 index 00000000000..4ee2fbff638 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/resource_generator.rb @@ -0,0 +1,76 @@ +class ResourceGenerator < Rails::Generator::NamedBase + default_options :skip_timestamps => false, :skip_migration => false + + attr_reader :controller_name, + :controller_class_path, + :controller_file_path, + :controller_class_nesting, + :controller_class_nesting_depth, + :controller_class_name, + :controller_singular_name, + :controller_plural_name + alias_method :controller_file_name, :controller_singular_name + alias_method :controller_table_name, :controller_plural_name + + def initialize(runtime_args, runtime_options = {}) + super + + @controller_name = @name.pluralize + + base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) + @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name) + + if @controller_class_nesting.empty? + @controller_class_name = @controller_class_name_without_nesting + else + @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" + end + end + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper") + m.class_collisions(class_name) + + # Controller, helper, views, and test directories. + m.directory(File.join('app/models', class_path)) + m.directory(File.join('app/controllers', controller_class_path)) + m.directory(File.join('app/helpers', controller_class_path)) + m.directory(File.join('app/views', controller_class_path, controller_file_name)) + m.directory(File.join('test/functional', controller_class_path)) + m.directory(File.join('test/unit', class_path)) + m.directory(File.join('test/unit/helpers', class_path)) + + m.dependency 'model', [name] + @args, :collision => :skip + + m.template( + 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") + ) + + m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) + m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) + m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) + + m.route_resources controller_file_name + end + end + + protected + def banner + "Usage: #{$0} resource ModelName [field:type, field:type]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-timestamps", + "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + end + + def model_name + class_name.demodulize + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/controller.rb new file mode 100644 index 00000000000..765a9426942 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/controller.rb @@ -0,0 +1,2 @@ +class <%= controller_class_name %>Controller < ApplicationController +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb new file mode 100644 index 00000000000..b1bb1dacbf9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= controller_class_name %>ControllerTest < ActionController::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper.rb new file mode 100644 index 00000000000..9bd821b1b24 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= controller_class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb new file mode 100644 index 00000000000..061f64a5e35 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= controller_class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/USAGE new file mode 100644 index 00000000000..810aea16f14 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/USAGE @@ -0,0 +1,29 @@ +Description: + Scaffolds an entire resource, from model and migration to controller and + views, along with a full test suite. The resource is ready to use as a + starting point for your RESTful, resource-oriented application. + + Pass the name of the model (in singular form), either CamelCased or + under_scored, as the first argument, and an optional list of attribute + pairs. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + You don't have to think up every attribute up front, but it helps to + sketch out a few so you can start working with the resource immediately. + + For example, 'scaffold post title:string body:text published:boolean' + gives you a model with those three attributes, a controller that handles + the create/show/update/destroy, forms to create and edit your posts, and + an index that lists them all, as well as a map.resources :posts + declaration in config/routes.rb. + + If you want to remove all the generated files, run + 'script/destroy scaffold ModelName'. + +Examples: + `./script/generate scaffold post` + `./script/generate scaffold post title:string body:text published:boolean` + `./script/generate scaffold purchase order_id:integer amount:decimal` diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb new file mode 100644 index 00000000000..88bc3252a06 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb @@ -0,0 +1,103 @@ +class ScaffoldGenerator < Rails::Generator::NamedBase + default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false + + attr_reader :controller_name, + :controller_class_path, + :controller_file_path, + :controller_class_nesting, + :controller_class_nesting_depth, + :controller_class_name, + :controller_underscore_name, + :controller_singular_name, + :controller_plural_name + alias_method :controller_file_name, :controller_underscore_name + alias_method :controller_table_name, :controller_plural_name + + def initialize(runtime_args, runtime_options = {}) + super + + if @name == @name.pluralize && !options[:force_plural] + logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural." + @name = @name.singularize + assign_names!(@name) + end + + @controller_name = @name.pluralize + + base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) + @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name) + @controller_singular_name=base_name.singularize + if @controller_class_nesting.empty? + @controller_class_name = @controller_class_name_without_nesting + else + @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" + end + end + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper") + m.class_collisions(class_name) + + # Controller, helper, views, test and stylesheets directories. + m.directory(File.join('app/models', class_path)) + m.directory(File.join('app/controllers', controller_class_path)) + m.directory(File.join('app/helpers', controller_class_path)) + m.directory(File.join('app/views', controller_class_path, controller_file_name)) + m.directory(File.join('app/views/layouts', controller_class_path)) + m.directory(File.join('test/functional', controller_class_path)) + m.directory(File.join('test/unit', class_path)) + m.directory(File.join('test/unit/helpers', class_path)) + m.directory(File.join('public/stylesheets', class_path)) + + for action in scaffold_views + m.template( + "view_#{action}.html.erb", + File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb") + ) + end + + # Layout and stylesheet. + m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb")) + m.template('style.css', 'public/stylesheets/scaffold.css') + + m.template( + 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") + ) + + m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) + m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) + m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) + + m.route_resources controller_file_name + + m.dependency 'model', [name] + @args, :collision => :skip + end + end + + protected + # Override with your own usage banner. + def banner + "Usage: #{$0} scaffold ModelName [field:type, field:type]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-timestamps", + "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + opt.on("--force-plural", + "Forces the generation of a plural ModelName") { |v| options[:force_plural] = v } + end + + def scaffold_views + %w[ index show new edit ] + end + + def model_name + class_name.demodulize + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb new file mode 100644 index 00000000000..4d190b9362d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb @@ -0,0 +1,85 @@ +class <%= controller_class_name %>Controller < ApplicationController + # GET /<%= table_name %> + # GET /<%= table_name %>.xml + def index + @<%= table_name %> = <%= class_name %>.all + + respond_to do |format| + format.html # index.html.erb + format.xml { render :xml => @<%= table_name %> } + end + end + + # GET /<%= table_name %>/1 + # GET /<%= table_name %>/1.xml + def show + @<%= file_name %> = <%= class_name %>.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @<%= file_name %> } + end + end + + # GET /<%= table_name %>/new + # GET /<%= table_name %>/new.xml + def new + @<%= file_name %> = <%= class_name %>.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @<%= file_name %> } + end + end + + # GET /<%= table_name %>/1/edit + def edit + @<%= file_name %> = <%= class_name %>.find(params[:id]) + end + + # POST /<%= table_name %> + # POST /<%= table_name %>.xml + def create + @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>]) + + respond_to do |format| + if @<%= file_name %>.save + flash[:notice] = '<%= class_name %> was successfully created.' + format.html { redirect_to(@<%= file_name %>) } + format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> } + else + format.html { render :action => "new" } + format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /<%= table_name %>/1 + # PUT /<%= table_name %>/1.xml + def update + @<%= file_name %> = <%= class_name %>.find(params[:id]) + + respond_to do |format| + if @<%= file_name %>.update_attributes(params[:<%= file_name %>]) + flash[:notice] = '<%= class_name %> was successfully updated.' + format.html { redirect_to(@<%= file_name %>) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /<%= table_name %>/1 + # DELETE /<%= table_name %>/1.xml + def destroy + @<%= file_name %> = <%= class_name %>.find(params[:id]) + @<%= file_name %>.destroy + + respond_to do |format| + format.html { redirect_to(<%= table_name %>_url) } + format.xml { head :ok } + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb new file mode 100644 index 00000000000..cd2fc578bf0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb @@ -0,0 +1,45 @@ +require 'test_helper' + +class <%= controller_class_name %>ControllerTest < ActionController::TestCase + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:<%= table_name %>) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create <%= file_name %>" do + assert_difference('<%= class_name %>.count') do + post :create, :<%= file_name %> => { } + end + + assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) + end + + test "should show <%= file_name %>" do + get :show, :id => <%= table_name %>(:one).to_param + assert_response :success + end + + test "should get edit" do + get :edit, :id => <%= table_name %>(:one).to_param + assert_response :success + end + + test "should update <%= file_name %>" do + put :update, :id => <%= table_name %>(:one).to_param, :<%= file_name %> => { } + assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) + end + + test "should destroy <%= file_name %>" do + assert_difference('<%= class_name %>.count', -1) do + delete :destroy, :id => <%= table_name %>(:one).to_param + end + + assert_redirected_to <%= table_name %>_path + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb new file mode 100644 index 00000000000..9bd821b1b24 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= controller_class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb new file mode 100644 index 00000000000..061f64a5e35 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= controller_class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb new file mode 100644 index 00000000000..ebc97f8130c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb @@ -0,0 +1,17 @@ + + + + + + <%= controller_class_name %>: <%%= controller.action_name %> + <%%= stylesheet_link_tag 'scaffold' %> + + + +

        <%%= flash[:notice] %>

        + +<%%= yield %> + + + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/style.css b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/style.css new file mode 100644 index 00000000000..093c20994d7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/style.css @@ -0,0 +1,54 @@ +body { background-color: #fff; color: #333; } + +body, p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { color: #000; } +a:visited { color: #666; } +a:hover { color: #fff; background-color:#000; } + +.fieldWithErrors { + padding: 2px; + background-color: red; + display: table; +} + +#errorExplanation { + width: 400px; + border: 2px solid red; + padding: 7px; + padding-bottom: 12px; + margin-bottom: 20px; + background-color: #f0f0f0; +} + +#errorExplanation h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + background-color: #c00; + color: #fff; +} + +#errorExplanation p { + color: #333; + margin-bottom: 0; + padding: 5px; +} + +#errorExplanation ul li { + font-size: 12px; + list-style: square; +} + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb new file mode 100644 index 00000000000..cca1d61c687 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb @@ -0,0 +1,18 @@ +

        Editing <%= singular_name %>

        + +<%% form_for(@<%= singular_name %>) do |f| %> + <%%= f.error_messages %> + +<% for attribute in attributes -%> +

        + <%%= f.label :<%= attribute.name %> %>
        + <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> +

        +<% end -%> +

        + <%%= f.submit 'Update' %> +

        +<%% end %> + +<%%= link_to 'Show', @<%= singular_name %> %> | +<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb new file mode 100644 index 00000000000..2e603d5b4a3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb @@ -0,0 +1,24 @@ +

        Listing <%= plural_name %>

        + + + +<% for attribute in attributes -%> + +<% end -%> + + +<%% @<%= plural_name %>.each do |<%= singular_name %>| %> + +<% for attribute in attributes -%> + +<% end -%> + + + + +<%% end %> +
        <%= attribute.column.human_name %>
        <%%=h <%= singular_name %>.<%= attribute.name %> %><%%= link_to 'Show', <%= singular_name %> %><%%= link_to 'Edit', edit_<%= singular_name %>_path(<%= singular_name %>) %><%%= link_to 'Destroy', <%= singular_name %>, :confirm => 'Are you sure?', :method => :delete %>
        + +
        + +<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb new file mode 100644 index 00000000000..96c89fc50ed --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb @@ -0,0 +1,17 @@ +

        New <%= singular_name %>

        + +<%% form_for(@<%= singular_name %>) do |f| %> + <%%= f.error_messages %> + +<% for attribute in attributes -%> +

        + <%%= f.label :<%= attribute.name %> %>
        + <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> +

        +<% end -%> +

        + <%%= f.submit 'Create' %> +

        +<%% end %> + +<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb new file mode 100644 index 00000000000..adecaf70c62 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb @@ -0,0 +1,10 @@ +<% for attribute in attributes -%> +

        + <%= attribute.column.human_name %>: + <%%=h @<%= singular_name %>.<%= attribute.name %> %> +

        + +<% end -%> + +<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> | +<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/USAGE new file mode 100644 index 00000000000..87117a3cb66 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/USAGE @@ -0,0 +1,10 @@ +Description: + Creates a migration to add the sessions table used by the Active Record + session store. Pass the migration name, either CamelCased or under_scored, + as an argument. + +Example: + `./script/generate session_migration CreateSessionTable` + + With 4 existing migrations, this creates the AddSessionTable migration + in db/migrate/005_add_session_table.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb new file mode 100644 index 00000000000..2e177033a18 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb @@ -0,0 +1,18 @@ +class SessionMigrationGenerator < Rails::Generator::NamedBase + def initialize(runtime_args, runtime_options = {}) + runtime_args << 'add_session_table' if runtime_args.empty? + super + end + + def manifest + record do |m| + m.migration_template 'migration.rb', 'db/migrate', + :assigns => { :session_table_name => default_session_table_name } + end + end + + protected + def default_session_table_name + ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session' + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb new file mode 100644 index 00000000000..ca220a5f233 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb @@ -0,0 +1,16 @@ +class <%= class_name %> < ActiveRecord::Migration + def self.up + create_table :<%= session_table_name %> do |t| + t.string :session_id, :null => false + t.text :data + t.timestamps + end + + add_index :<%= session_table_name %>, :session_id + add_index :<%= session_table_name %>, :updated_at + end + + def self.down + drop_table :<%= session_table_name %> + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/lookup.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/lookup.rb new file mode 100644 index 00000000000..a3525364a2f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/lookup.rb @@ -0,0 +1,249 @@ +require 'pathname' + +require File.dirname(__FILE__) + '/spec' + +class Object + class << self + # Lookup missing generators using const_missing. This allows any + # generator to reference another without having to know its location: + # RubyGems, ~/.rails/generators, and RAILS_ROOT/generators. + def lookup_missing_generator(class_id) + if md = /(.+)Generator$/.match(class_id.to_s) + name = md.captures.first.demodulize.underscore + Rails::Generator::Base.lookup(name).klass + else + const_missing_before_generators(class_id) + end + end + + unless respond_to?(:const_missing_before_generators) + alias_method :const_missing_before_generators, :const_missing + alias_method :const_missing, :lookup_missing_generator + end + end +end + +# User home directory lookup adapted from RubyGems. +def Dir.user_home + if ENV['HOME'] + ENV['HOME'] + elsif ENV['USERPROFILE'] + ENV['USERPROFILE'] + elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH'] + "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" + else + File.expand_path '~' + end +end + + +module Rails + module Generator + + # Generator lookup is managed by a list of sources which return specs + # describing where to find and how to create generators. This module + # provides class methods for manipulating the source list and looking up + # generator specs, and an #instance wrapper for quickly instantiating + # generators by name. + # + # A spec is not a generator: it's a description of where to find + # the generator and how to create it. A source is anything that + # yields generators from #each. PathSource and GemGeneratorSource are provided. + module Lookup + def self.included(base) + base.extend(ClassMethods) + base.use_component_sources! + end + + # Convenience method to instantiate another generator. + def instance(generator_name, args, runtime_options = {}) + self.class.instance(generator_name, args, runtime_options) + end + + module ClassMethods + # The list of sources where we look, in order, for generators. + def sources + read_inheritable_attribute(:sources) or use_component_sources! + end + + # Add a source to the end of the list. + def append_sources(*args) + sources.concat(args.flatten) + invalidate_cache! + end + + # Add a source to the beginning of the list. + def prepend_sources(*args) + write_inheritable_array(:sources, args.flatten + sources) + invalidate_cache! + end + + # Reset the source list. + def reset_sources + write_inheritable_attribute(:sources, []) + invalidate_cache! + end + + # Use application generators (app, ?). + def use_application_sources! + reset_sources + sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications") + end + + # Use component generators (model, controller, etc). + # 1. Rails application. If RAILS_ROOT is defined we know we're + # generating in the context of a Rails application, so search + # RAILS_ROOT/generators. + # 2. Look in plugins, either for generators/ or rails_generators/ + # directories within each plugin + # 3. User home directory. Search ~/.rails/generators. + # 4. RubyGems. Search for gems named *_generator, and look for + # generators within any RubyGem's + # /rails_generators/_generator.rb file. + # 5. Builtins. Model, controller, mailer, scaffold, and so on. + def use_component_sources! + reset_sources + if defined? ::RAILS_ROOT + sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators") + sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators") + Rails.configuration.plugin_paths.each do |path| + relative_path = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(::RAILS_ROOT)) + sources << PathSource.new(:"plugins (#{relative_path})", "#{path}/*/**/{,rails_}generators") + end + end + sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators") + if Object.const_defined?(:Gem) + sources << GemGeneratorSource.new + sources << GemPathSource.new + end + sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components") + end + + # Lookup knows how to find generators' Specs from a list of Sources. + # Searches the sources, in order, for the first matching name. + def lookup(generator_name) + @found ||= {} + generator_name = generator_name.to_s.downcase + @found[generator_name] ||= cache.find { |spec| spec.name == generator_name } + unless @found[generator_name] + chars = generator_name.scan(/./).map{|c|"#{c}.*?"} + rx = /^#{chars}$/ + gns = cache.select{|spec| spec.name =~ rx } + @found[generator_name] ||= gns.first if gns.length == 1 + raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1 + end + @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator" + end + + # Convenience method to lookup and instantiate a generator. + def instance(generator_name, args = [], runtime_options = {}) + lookup(generator_name).klass.new(args, full_options(runtime_options)) + end + + private + # Lookup and cache every generator from the source list. + def cache + @cache ||= sources.inject([]) { |cache, source| cache + source.to_a } + end + + # Clear the cache whenever the source list changes. + def invalidate_cache! + @cache = nil + end + end + end + + # Sources enumerate (yield from #each) generator specs which describe + # where to find and how to create generators. Enumerable is mixed in so, + # for example, source.collect will retrieve every generator. + # Sources may be assigned a label to distinguish them. + class Source + include Enumerable + + attr_reader :label + def initialize(label) + @label = label + end + + # The each method must be implemented in subclasses. + # The base implementation raises an error. + def each + raise NotImplementedError + end + + # Return a convenient sorted list of all generator names. + def names + map { |spec| spec.name }.sort + end + end + + + # PathSource looks for generators in a filesystem directory. + class PathSource < Source + attr_reader :path + + def initialize(label, path) + super label + @path = path + end + + # Yield each eligible subdirectory. + def each + Dir["#{path}/[a-z]*"].each do |dir| + if File.directory?(dir) + yield Spec.new(File.basename(dir), dir, label) + end + end + end + end + + class AbstractGemSource < Source + def initialize + super :RubyGems + end + end + + # GemGeneratorSource hits the mines to quarry for generators. The latest versions + # of gems named *_generator are selected. + class GemGeneratorSource < AbstractGemSource + # Yield latest versions of generator gems. + def each + dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default) + Gem::cache.search(dependency).inject({}) { |latest, gem| + hem = latest[gem.name] + latest[gem.name] = gem if hem.nil? or gem.version > hem.version + latest + }.values.each { |gem| + yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label) + } + end + end + + # GemPathSource looks for generators within any RubyGem's /rails_generators/_generator.rb file. + class GemPathSource < AbstractGemSource + # Yield each generator within rails_generator subdirectories. + def each + generator_full_paths.each do |generator| + yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label) + end + end + + private + def generator_full_paths + @generator_full_paths ||= + Gem::cache.inject({}) do |latest, name_gem| + name, gem = name_gem + hem = latest[gem.name] + latest[gem.name] = gem if hem.nil? or gem.version > hem.version + latest + end.values.inject([]) do |mem, gem| + Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator| + mem << generator + end + mem + end + end + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/manifest.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/manifest.rb new file mode 100644 index 00000000000..702effa76f5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/manifest.rb @@ -0,0 +1,53 @@ +module Rails + module Generator + + # Manifest captures the actions a generator performs. Instantiate + # a manifest with an optional target object, hammer it with actions, + # then replay or rewind on the object of your choice. + # + # Example: + # manifest = Manifest.new { |m| + # m.make_directory '/foo' + # m.create_file '/foo/bar.txt' + # } + # manifest.replay(creator) + # manifest.rewind(destroyer) + class Manifest + attr_reader :target + + # Take a default action target. Yield self if block given. + def initialize(target = nil) + @target, @actions = target, [] + yield self if block_given? + end + + # Record an action. + def method_missing(action, *args, &block) + @actions << [action, args, block] + end + + # Replay recorded actions. + def replay(target = nil) + send_actions(target || @target, @actions) + end + + # Rewind recorded actions. + def rewind(target = nil) + send_actions(target || @target, @actions.reverse) + end + + # Erase recorded actions. + def erase + @actions = [] + end + + private + def send_actions(target, actions) + actions.each do |method, args, block| + target.send(method, *args, &block) + end + end + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/options.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/options.rb new file mode 100644 index 00000000000..070f96b69b9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/options.rb @@ -0,0 +1,150 @@ +require 'optparse' + +module Rails + module Generator + module Options + def self.included(base) + base.extend(ClassMethods) + class << base + if respond_to?(:inherited) + alias_method :inherited_without_options, :inherited + end + alias_method :inherited, :inherited_with_options + end + end + + module ClassMethods + def inherited_with_options(sub) + inherited_without_options(sub) if respond_to?(:inherited_without_options) + sub.extend(Rails::Generator::Options::ClassMethods) + end + + def mandatory_options(options = nil) + if options + write_inheritable_attribute(:mandatory_options, options) + else + read_inheritable_attribute(:mandatory_options) or write_inheritable_attribute(:mandatory_options, {}) + end + end + + def default_options(options = nil) + if options + write_inheritable_attribute(:default_options, options) + else + read_inheritable_attribute(:default_options) or write_inheritable_attribute(:default_options, {}) + end + end + + # Merge together our class options. In increasing precedence: + # default_options (class default options) + # runtime_options (provided as argument) + # mandatory_options (class mandatory options) + def full_options(runtime_options = {}) + default_options.merge(runtime_options).merge(mandatory_options) + end + + end + + # Each instance has an options hash that's populated by #parse. + def options + @options ||= {} + end + attr_writer :options + + protected + # Convenient access to class mandatory options. + def mandatory_options + self.class.mandatory_options + end + + # Convenient access to class default options. + def default_options + self.class.default_options + end + + # Merge together our instance options. In increasing precedence: + # default_options (class default options) + # options (instance options) + # runtime_options (provided as argument) + # mandatory_options (class mandatory options) + def full_options(runtime_options = {}) + self.class.full_options(options.merge(runtime_options)) + end + + # Parse arguments into the options hash. Classes may customize + # parsing behavior by overriding these methods: + # #banner Usage: ./script/generate [options] + # #add_options! Options: + # some options.. + # #add_general_options! General Options: + # general options.. + def parse!(args, runtime_options = {}) + self.options = {} + + @option_parser = OptionParser.new do |opt| + opt.banner = banner + add_options!(opt) + add_general_options!(opt) + opt.parse!(args) + end + + return args + ensure + self.options = full_options(runtime_options) + end + + # Raise a usage error. Override usage_message to provide a blurb + # after the option parser summary. + def usage(message = usage_message) + raise UsageError, "#{@option_parser}\n#{message}" + end + + def usage_message + '' + end + + # Override with your own usage banner. + def banner + "Usage: #{$0} [options]" + end + + # Override to add your options to the parser: + # def add_options!(opt) + # opt.on('-v', '--verbose') { |value| options[:verbose] = value } + # end + def add_options!(opt) + end + + # Adds general options like -h and --quiet. Usually don't override. + def add_general_options!(opt) + opt.separator '' + opt.separator 'Rails Info:' + opt.on('-v', '--version', 'Show the Rails version number and quit.') + opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v } + + opt.separator '' + opt.separator 'General Options:' + + opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v } + opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force } + opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip } + opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v } + opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v } + opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do + options[:svn] = {} + `svn status`.each_line do |line| + options[:svn][line.chomp[7..-1]] = true + end + end + opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do + options[:git] = {:new => {}, :modified => {}} + `git status`.each_line do |line| + options[:git][:new][line.chomp[14..-1]] = true if line =~ /new file:/ + options[:git][:modified][line.chomp[14..-1]] = true if line =~ /modified:/ + end + end + end + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/scripts.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/scripts.rb new file mode 100644 index 00000000000..9b1a99838a6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/scripts.rb @@ -0,0 +1,89 @@ +require File.dirname(__FILE__) + '/options' + +module Rails + module Generator + module Scripts + + # Generator scripts handle command-line invocation. Each script + # responds to an invoke! class method which handles option parsing + # and generator invocation. + class Base + include Options + default_options :collision => :ask, :quiet => false + + # Run the generator script. Takes an array of unparsed arguments + # and a hash of parsed arguments, takes the generator as an option + # or first remaining argument, and invokes the requested command. + def run(args = [], runtime_options = {}) + begin + parse!(args.dup, runtime_options) + rescue OptionParser::InvalidOption => e + # Don't cry, script. Generators want what you think is invalid. + end + + # Generator name is the only required option. + unless options[:generator] + usage if args.empty? + options[:generator] ||= args.shift + end + + # Look up generator instance and invoke command on it. + Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke! + rescue => e + puts e + puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace] + raise SystemExit + end + + protected + # Override with your own script usage banner. + def banner + "Usage: #{$0} generator [options] [args]" + end + + def usage_message + usage = "\nInstalled Generators\n" + Rails::Generator::Base.sources.inject([]) do |mem, source| + # Using an association list instead of a hash to preserve order, + # for aesthetic reasons more than anything else. + label = source.label.to_s.capitalize + pair = mem.assoc(label) + mem << (pair = [label, []]) if pair.nil? + pair[1] |= source.names + mem + end.each do |label, names| + usage << " #{label}: #{names.join(', ')}\n" unless names.empty? + end + + usage << < :destroy + + protected + def usage_message + usage = "\nInstalled Generators\n" + Rails::Generator::Base.sources.each do |source| + label = source.label.to_s.capitalize + names = source.names + usage << " #{label}: #{names.join(', ')}\n" unless names.empty? + end + + usage << < :create + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/scripts/update.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/scripts/update.rb new file mode 100644 index 00000000000..53a9faa3663 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/scripts/update.rb @@ -0,0 +1,12 @@ +require File.dirname(__FILE__) + '/../scripts' + +module Rails::Generator::Scripts + class Update < Base + mandatory_options :command => :update + + protected + def banner + "Usage: #{$0} [options] scaffold" + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/secret_key_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/secret_key_generator.rb new file mode 100644 index 00000000000..7dd495a2f50 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/secret_key_generator.rb @@ -0,0 +1,24 @@ +require 'active_support/deprecation' + +module Rails + # A class for creating random secret keys. This class will do its best to create a + # random secret key that's as secure as possible, using whatever methods are + # available on the current platform. For example: + # + # generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name") + # generator.generate_secret # => "f3f1be90053fa851... (some long string)" + # + # This class is *deprecated* in Rails 2.2 in favor of ActiveSupport::SecureRandom. + # It is currently a wrapper around ActiveSupport::SecureRandom. + class SecretKeyGenerator + def initialize(identifier) + end + + # Generate a random secret key with the best possible method available on + # the current platform. + def generate_secret + ActiveSupport::SecureRandom.hex(64) + end + deprecate :generate_secret=>"You should use ActiveSupport::SecureRandom.hex(64)" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/simple_logger.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/simple_logger.rb new file mode 100644 index 00000000000..d750f07b84f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/simple_logger.rb @@ -0,0 +1,46 @@ +module Rails + module Generator + class SimpleLogger # :nodoc: + attr_reader :out + attr_accessor :quiet + + def initialize(out = $stdout) + @out = out + @quiet = false + @level = 0 + end + + def log(status, message, &block) + @out.print("%12s %s%s\n" % [status, ' ' * @level, message]) unless quiet + indent(&block) if block_given? + end + + def indent(&block) + @level += 1 + if block_given? + begin + block.call + ensure + outdent + end + end + end + + def outdent + @level -= 1 + if block_given? + begin + block.call + ensure + indent + end + end + end + + private + def method_missing(method, *args, &block) + log(method.to_s, args.first, &block) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/spec.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/spec.rb new file mode 100644 index 00000000000..9d780b7ac5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rails_generator/spec.rb @@ -0,0 +1,44 @@ +module Rails + module Generator + # A spec knows where a generator was found and how to instantiate it. + # Metadata include the generator's name, its base path, and the source + # which yielded it (PathSource, GemPathSource, etc.) + class Spec + attr_reader :name, :path, :source + + def initialize(name, path, source) + @name, @path, @source = name, path, source + end + + # Look up the generator class. Require its class file, find the class + # in ObjectSpace, tag it with this spec, and return. + def klass + unless @klass + require class_file + @klass = lookup_class + @klass.spec = self + end + @klass + end + + def class_file + "#{path}/#{name}_generator.rb" + end + + def class_name + "#{name.camelize}Generator" + end + + private + # Search for the first Class descending from Rails::Generator::Base + # whose name matches the requested class name. + def lookup_class + ObjectSpace.each_object(Class) do |obj| + return obj if obj.ancestors.include?(Rails::Generator::Base) and + obj.name.split('::').last == class_name + end + raise NameError, "Missing #{class_name} class in #{class_file}" + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/railties_path.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/railties_path.rb new file mode 100644 index 00000000000..a298a4cc275 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/railties_path.rb @@ -0,0 +1 @@ +RAILTIES_PATH = File.join(File.dirname(__FILE__), '..') diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/ruby_version_check.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/ruby_version_check.rb new file mode 100644 index 00000000000..68d3acc8764 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/ruby_version_check.rb @@ -0,0 +1,17 @@ +min_release = "1.8.2 (2004-12-25)" +ruby_release = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" +if ruby_release =~ /1\.8\.3/ + abort <<-end_message + + Rails does not work with Ruby version 1.8.3. + Please upgrade to version 1.8.4 or downgrade to 1.8.2. + + end_message +elsif ruby_release < min_release + abort <<-end_message + + Rails requires Ruby version #{min_release} or later. + You're running #{ruby_release}; please upgrade to continue. + + end_message +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rubyprof_ext.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rubyprof_ext.rb new file mode 100644 index 00000000000..f6e90357cee --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/rubyprof_ext.rb @@ -0,0 +1,35 @@ +require 'prof' + +module Prof #:nodoc: + # Adapted from Shugo Maeda's unprof.rb + def self.print_profile(results, io = $stderr) + total = results.detect { |i| + i.method_class.nil? && i.method_id == :"#toplevel" + }.total_time + total = 0.001 if total < 0.001 + + io.puts " %% cumulative self self total" + io.puts " time seconds seconds calls ms/call ms/call name" + + sum = 0.0 + for r in results + sum += r.self_time + + name = if r.method_class.nil? + r.method_id.to_s + elsif r.method_class.is_a?(Class) + "#{r.method_class}##{r.method_id}" + else + "#{r.method_class}.#{r.method_id}" + end + io.printf "%6.2f %8.3f %8.3f %8d %8.2f %8.2f %s\n", + r.self_time / total * 100, + sum, + r.self_time, + r.count, + r.self_time * 1000 / r.count, + r.total_time * 1000 / r.count, + name + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/source_annotation_extractor.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/source_annotation_extractor.rb new file mode 100644 index 00000000000..591fd6f6bd3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/source_annotation_extractor.rb @@ -0,0 +1,102 @@ +# Implements the logic behind the rake tasks for annotations like +# +# rake notes +# rake notes:optimize +# +# and friends. See rake -T notes and railties/lib/tasks/annotations.rake. +# +# Annotation objects are triplets :line, :tag, :text that +# represent the line where the annotation lives, its tag, and its text. Note +# the filename is not stored. +# +# Annotations are looked for in comments and modulus whitespace they have to +# start with the tag optionally followed by a colon. Everything up to the end +# of the line (or closing ERb comment tag) is considered to be their text. +class SourceAnnotationExtractor + class Annotation < Struct.new(:line, :tag, :text) + + # Returns a representation of the annotation that looks like this: + # + # [126] [TODO] This algorithm is simple and clearly correct, make it faster. + # + # If +options+ has a flag :tag the tag is shown as in the example above. + # Otherwise the string contains just line and text. + def to_s(options={}) + s = "[%3d] " % line + s << "[#{tag}] " if options[:tag] + s << text + end + end + + # Prints all annotations with tag +tag+ under the root directories +app+, +lib+, + # and +test+ (recursively). Only filenames with extension +.builder+, +.rb+, + # +.rxml+, +.rjs+, +.rhtml+, or +.erb+ are taken into account. The +options+ + # hash is passed to each annotation's +to_s+. + # + # This class method is the single entry point for the rake tasks. + def self.enumerate(tag, options={}) + extractor = new(tag) + extractor.display(extractor.find, options) + end + + attr_reader :tag + + def initialize(tag) + @tag = tag + end + + # Returns a hash that maps filenames under +dirs+ (recursively) to arrays + # with their annotations. Only files with annotations are included, and only + # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+ + # are taken into account. + def find(dirs=%w(app lib test)) + dirs.inject({}) { |h, dir| h.update(find_in(dir)) } + end + + # Returns a hash that maps filenames under +dir+ (recursively) to arrays + # with their annotations. Only files with annotations are included, and only + # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+ + # are taken into account. + def find_in(dir) + results = {} + + Dir.glob("#{dir}/*") do |item| + next if File.basename(item)[0] == ?. + + if File.directory?(item) + results.update(find_in(item)) + elsif item =~ /\.(builder|(r(?:b|xml|js)))$/ + results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/)) + elsif item =~ /\.(rhtml|erb)$/ + results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/)) + end + end + + results + end + + # If +file+ is the filename of a file that contains annotations this method returns + # a hash with a single entry that maps +file+ to an array of its annotations. + # Otherwise it returns an empty hash. + def extract_annotations_from(file, pattern) + lineno = 0 + result = File.readlines(file).inject([]) do |list, line| + lineno += 1 + next list unless line =~ pattern + list << Annotation.new(lineno, $1, $2) + end + result.empty? ? {} : { file => result } + end + + # Prints the mapping from filenames to annotations in +results+ ordered by filename. + # The +options+ hash is passed to each annotation's +to_s+. + def display(results, options={}) + results.keys.sort.each do |file| + puts "#{file}:" + results[file].each do |note| + puts " * #{note.to_s(options)}" + end + puts + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/annotations.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/annotations.rake new file mode 100644 index 00000000000..48ac40099a0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/annotations.rake @@ -0,0 +1,20 @@ +require 'source_annotation_extractor' + +desc "Enumerate all annotations" +task :notes do + SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true +end + +namespace :notes do + ["OPTIMIZE", "FIXME", "TODO"].each do |annotation| + desc "Enumerate all #{annotation} annotations" + task annotation.downcase.intern do + SourceAnnotationExtractor.enumerate annotation + end + end + + desc "Enumerate a custom annotation, specify with ANNOTATION=WTFHAX" + task :custom do + SourceAnnotationExtractor.enumerate ENV['ANNOTATION'] + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/databases.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/databases.rake new file mode 100644 index 00000000000..8b608396417 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/databases.rake @@ -0,0 +1,436 @@ +namespace :db do + task :load_config => :rails_env do + require 'active_record' + ActiveRecord::Base.configurations = Rails::Configuration.new.database_configuration + end + + namespace :create do + desc 'Create all the local databases defined in config/database.yml' + task :all => :load_config do + ActiveRecord::Base.configurations.each_value do |config| + # Skip entries that don't have a database key, such as the first entry here: + # + # defaults: &defaults + # adapter: mysql + # username: root + # password: + # host: localhost + # + # development: + # database: blog_development + # <<: *defaults + next unless config['database'] + # Only connect to local databases + local_database?(config) { create_database(config) } + end + end + end + + desc 'Create the database defined in config/database.yml for the current RAILS_ENV' + task :create => :load_config do + create_database(ActiveRecord::Base.configurations[RAILS_ENV]) + end + + def create_database(config) + begin + if config['adapter'] =~ /sqlite/ + if File.exist?(config['database']) + $stderr.puts "#{config['database']} already exists" + else + begin + # Create the SQLite database + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection + rescue + $stderr.puts $!, *($!.backtrace) + $stderr.puts "Couldn't create database for #{config.inspect}" + end + end + return # Skip the else clause of begin/rescue + else + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection + end + rescue + case config['adapter'] + when 'mysql' + @charset = ENV['CHARSET'] || 'utf8' + @collation = ENV['COLLATION'] || 'utf8_unicode_ci' + begin + ActiveRecord::Base.establish_connection(config.merge('database' => nil)) + ActiveRecord::Base.connection.create_database(config['database'], :charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)) + ActiveRecord::Base.establish_connection(config) + rescue + $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation} (if you set the charset manually, make sure you have a matching collation)" + end + when 'postgresql' + @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' + begin + ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) + ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding)) + ActiveRecord::Base.establish_connection(config) + rescue + $stderr.puts $!, *($!.backtrace) + $stderr.puts "Couldn't create database for #{config.inspect}" + end + end + else + $stderr.puts "#{config['database']} already exists" + end + end + + namespace :drop do + desc 'Drops all the local databases defined in config/database.yml' + task :all => :load_config do + ActiveRecord::Base.configurations.each_value do |config| + # Skip entries that don't have a database key + next unless config['database'] + # Only connect to local databases + local_database?(config) { drop_database(config) } + end + end + end + + desc 'Drops the database for the current RAILS_ENV' + task :drop => :load_config do + config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + begin + drop_database(config) + rescue Exception => e + puts "Couldn't drop #{config['database']} : #{e.inspect}" + end + end + + def local_database?(config, &block) + if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank? + yield + else + puts "This task only modifies local databases. #{config['database']} is on a remote host." + end + end + + + desc "Migrate the database through scripts in db/migrate and update db/schema.rb by invoking db:schema:dump. Target specific version with VERSION=x. Turn off output with VERBOSE=false." + task :migrate => :environment do + ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true + ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + namespace :migrate do + desc 'Rollbacks the database one migration and re migrate up. If you want to rollback more than one step, define STEP=x. Target specific version with VERSION=x.' + task :redo => :environment do + if ENV["VERSION"] + Rake::Task["db:migrate:down"].invoke + Rake::Task["db:migrate:up"].invoke + else + Rake::Task["db:rollback"].invoke + Rake::Task["db:migrate"].invoke + end + end + + desc 'Resets your database using your migrations for the current environment' + task :reset => ["db:drop", "db:create", "db:migrate"] + + desc 'Runs the "up" for a given migration VERSION.' + task :up => :environment do + version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil + raise "VERSION is required" unless version + ActiveRecord::Migrator.run(:up, "db/migrate/", version) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + desc 'Runs the "down" for a given migration VERSION.' + task :down => :environment do + version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil + raise "VERSION is required" unless version + ActiveRecord::Migrator.run(:down, "db/migrate/", version) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + end + + desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n' + task :rollback => :environment do + step = ENV['STEP'] ? ENV['STEP'].to_i : 1 + ActiveRecord::Migrator.rollback('db/migrate/', step) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' + task :reset => [ 'db:drop', 'db:setup' ] + + desc "Retrieves the charset for the current environment's database" + task :charset => :environment do + config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + puts ActiveRecord::Base.connection.charset + when 'postgresql' + ActiveRecord::Base.establish_connection(config) + puts ActiveRecord::Base.connection.encoding + else + puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' + end + end + + desc "Retrieves the collation for the current environment's database" + task :collation => :environment do + config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + puts ActiveRecord::Base.connection.collation + else + puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' + end + end + + desc "Retrieves the current schema version number" + task :version => :environment do + puts "Current version: #{ActiveRecord::Migrator.current_version}" + end + + desc "Raises an error if there are pending migrations" + task :abort_if_pending_migrations => :environment do + if defined? ActiveRecord + pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations + + if pending_migrations.any? + puts "You have #{pending_migrations.size} pending migrations:" + pending_migrations.each do |pending_migration| + puts ' %4d %s' % [pending_migration.version, pending_migration.name] + end + abort %{Run "rake db:migrate" to update your database then try again.} + end + end + end + + desc 'Create the database, load the schema, and initialize with the seed data' + task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] + + desc 'Load the seed data from db/seeds.rb' + task :seed => :environment do + seed_file = File.join(Rails.root, 'db', 'seeds.rb') + load(seed_file) if File.exist?(seed_file) + end + + namespace :fixtures do + desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." + task :load => :environment do + require 'active_record/fixtures' + ActiveRecord::Base.establish_connection(Rails.env) + base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') + fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir + + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| + Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) + end + end + + desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." + task :identify => :environment do + require "active_record/fixtures" + + label, id = ENV["LABEL"], ENV["ID"] + raise "LABEL or ID required" if label.blank? && id.blank? + + puts %Q(The fixture ID for "#{label}" is #{Fixtures.identify(label)}.) if label + + base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') + Dir["#{base_dir}/**/*.yml"].each do |file| + if data = YAML::load(ERB.new(IO.read(file)).result) + data.keys.each do |key| + key_id = Fixtures.identify(key) + + if key == label || key_id == id.to_i + puts "#{file}: #{key} (#{key_id})" + end + end + end + end + end + end + + namespace :schema do + desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" + task :dump => :environment do + require 'active_record/schema_dumper' + File.open(ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb", "w") do |file| + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) + end + Rake::Task["db:schema:dump"].reenable + end + + desc "Load a schema.rb file into the database" + task :load => :environment do + file = ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb" + if File.exists?(file) + load(file) + else + abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{RAILS_ROOT}/config/environment.rb to prevent active_record from loading: config.frameworks -= [ :active_record ]} + end + end + end + + namespace :structure do + desc "Dump the database structure to a SQL file" + task :dump => :environment do + abcs = ActiveRecord::Base.configurations + case abcs[RAILS_ENV]["adapter"] + when "mysql", "oci", "oracle" + ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) + File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + when "postgresql" + ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"] + ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"] + ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"] + search_path = abcs[RAILS_ENV]["schema_search_path"] + search_path = "--schema=#{search_path}" if search_path + `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}` + raise "Error dumping database" if $?.exitstatus == 1 + when "sqlite", "sqlite3" + dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"] + `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql` + when "sqlserver" + `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r` + `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r` + when "firebird" + set_firebird_env(abcs[RAILS_ENV]) + db_string = firebird_db_string(abcs[RAILS_ENV]) + sh "isql -a #{db_string} > #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql" + else + raise "Task not supported by '#{abcs["test"]["adapter"]}'" + end + + if ActiveRecord::Base.connection.supports_migrations? + File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } + end + end + end + + namespace :test do + desc "Recreate the test database from the current schema.rb" + task :load => 'db:test:purge' do + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) + ActiveRecord::Schema.verbose = false + Rake::Task["db:schema:load"].invoke + end + + desc "Recreate the test database from the current environment's database schema" + task :clone => %w(db:schema:dump db:test:load) + + desc "Recreate the test databases from the development structure" + task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do + abcs = ActiveRecord::Base.configurations + case abcs["test"]["adapter"] + when "mysql" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') + IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| + ActiveRecord::Base.connection.execute(table) + end + when "postgresql" + ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] + ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] + ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] + `psql -U "#{abcs["test"]["username"]}" -f #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}` + when "sqlite", "sqlite3" + dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] + `#{abcs["test"]["adapter"]} #{dbfile} < #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql` + when "sqlserver" + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` + when "oci", "oracle" + ActiveRecord::Base.establish_connection(:test) + IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| + ActiveRecord::Base.connection.execute(ddl) + end + when "firebird" + set_firebird_env(abcs["test"]) + db_string = firebird_db_string(abcs["test"]) + sh "isql -i #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{db_string}" + else + raise "Task not supported by '#{abcs["test"]["adapter"]}'" + end + end + + desc "Empty the test database" + task :purge => :environment do + abcs = ActiveRecord::Base.configurations + case abcs["test"]["adapter"] + when "mysql" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"]) + when "postgresql" + ActiveRecord::Base.clear_active_connections! + drop_database(abcs['test']) + create_database(abcs['test']) + when "sqlite","sqlite3" + dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] + File.delete(dbfile) if File.exist?(dbfile) + when "sqlserver" + dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` + when "oci", "oracle" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl| + ActiveRecord::Base.connection.execute(ddl) + end + when "firebird" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.recreate_database! + else + raise "Task not supported by '#{abcs["test"]["adapter"]}'" + end + end + + desc 'Check for pending migrations and load the test schema' + task :prepare => 'db:abort_if_pending_migrations' do + if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank? + Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke + end + end + end + + namespace :sessions do + desc "Creates a sessions migration for use with ActiveRecord::SessionStore" + task :create => :environment do + raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations? + require 'rails_generator' + require 'rails_generator/scripts/generate' + Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "CreateSessions"]) + end + + desc "Clear the sessions table" + task :clear => :environment do + ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}" + end + end +end + +def drop_database(config) + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection.drop_database config['database'] + when /^sqlite/ + FileUtils.rm(File.join(RAILS_ROOT, config['database'])) + when 'postgresql' + ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) + ActiveRecord::Base.connection.drop_database config['database'] + end +end + +def session_table_name + ActiveRecord::Base.pluralize_table_names ? :sessions : :session +end + +def set_firebird_env(config) + ENV["ISC_USER"] = config["username"].to_s if config["username"] + ENV["ISC_PASSWORD"] = config["password"].to_s if config["password"] +end + +def firebird_db_string(config) + FireRuby::Database.db_string_for(config.symbolize_keys) +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/documentation.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/documentation.rake new file mode 100644 index 00000000000..8b41478a93d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/documentation.rake @@ -0,0 +1,88 @@ +namespace :doc do + desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb or title with TITLE=\"Custom Title\"" + Rake::RDocTask.new("app") { |rdoc| + rdoc.rdoc_dir = 'doc/app' + rdoc.template = ENV['template'] if ENV['template'] + rdoc.title = ENV['title'] || "Rails Application Documentation" + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.options << '--charset' << 'utf-8' + rdoc.rdoc_files.include('doc/README_FOR_APP') + rdoc.rdoc_files.include('app/**/*.rb') + rdoc.rdoc_files.include('lib/**/*.rb') + } + + desc "Generate documentation for the Rails framework" + Rake::RDocTask.new("rails") { |rdoc| + rdoc.rdoc_dir = 'doc/api' + rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.title = "Rails Framework Documentation" + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('vendor/rails/railties/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/railties/MIT-LICENSE') + rdoc.rdoc_files.include('vendor/rails/railties/README') + rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,rails_generator/*.rb}') + rdoc.rdoc_files.include('vendor/rails/activerecord/README') + rdoc.rdoc_files.include('vendor/rails/activerecord/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/activerecord/lib/active_record/**/*.rb') + rdoc.rdoc_files.exclude('vendor/rails/activerecord/lib/active_record/vendor/*') + rdoc.rdoc_files.include('vendor/rails/activeresource/README') + rdoc.rdoc_files.include('vendor/rails/activeresource/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/activeresource/lib/active_resource.rb') + rdoc.rdoc_files.include('vendor/rails/activeresource/lib/active_resource/*') + rdoc.rdoc_files.include('vendor/rails/actionpack/README') + rdoc.rdoc_files.include('vendor/rails/actionpack/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_controller/**/*.rb') + rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_view/**/*.rb') + rdoc.rdoc_files.include('vendor/rails/actionmailer/README') + rdoc.rdoc_files.include('vendor/rails/actionmailer/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/actionmailer/lib/action_mailer/base.rb') + rdoc.rdoc_files.include('vendor/rails/activesupport/README') + rdoc.rdoc_files.include('vendor/rails/activesupport/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/activesupport/lib/active_support/**/*.rb') + } + + plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) } + + desc "Generate documentation for all installed plugins" + task :plugins => plugins.collect { |plugin| "doc:plugins:#{plugin}" } + + desc "Remove plugin documentation" + task :clobber_plugins do + rm_rf 'doc/plugins' rescue nil + end + + desc "Generate Rails guides" + task :guides do + require File.join(RAILTIES_PATH, "guides/rails_guides") + RailsGuides::Generator.new(File.join(RAILS_ROOT, "doc/guides")).generate + end + + namespace :plugins do + # Define doc tasks for each plugin + plugins.each do |plugin| + desc "Generate documentation for the #{plugin} plugin" + task(plugin => :environment) do + plugin_base = "vendor/plugins/#{plugin}" + options = [] + files = Rake::FileList.new + options << "-o doc/plugins/#{plugin}" + options << "--title '#{plugin.titlecase} Plugin Documentation'" + options << '--line-numbers' << '--inline-source' + options << '--charset' << 'utf-8' + options << '-T html' + + files.include("#{plugin_base}/lib/**/*.rb") + if File.exist?("#{plugin_base}/README") + files.include("#{plugin_base}/README") + options << "--main '#{plugin_base}/README'" + end + files.include("#{plugin_base}/CHANGELOG") if File.exist?("#{plugin_base}/CHANGELOG") + + options << files.to_s + + sh %(rdoc #{options * ' '}) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/framework.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/framework.rake new file mode 100644 index 00000000000..191c9361ff2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/framework.rake @@ -0,0 +1,143 @@ +namespace :rails do + namespace :freeze do + desc "Lock this application to the current gems (by unpacking them into vendor/rails)" + task :gems do + deps = %w(actionpack activerecord actionmailer activesupport activeresource) + require 'rubygems' + require 'rubygems/gem_runner' + + rails = (version = ENV['VERSION']) ? + Gem.cache.find_name('rails', "= #{version}").first : + Gem.cache.find_name('rails').sort_by { |g| g.version }.last + + version ||= rails.version + + unless rails + puts "No rails gem #{version} is installed. Do 'gem list rails' to see what you have available." + exit + end + + puts "Freezing to the gems for Rails #{rails.version}" + rm_rf "vendor/rails" + mkdir_p "vendor/rails" + + begin + chdir("vendor/rails") do + rails.dependencies.select { |g| deps.include? g.name }.each do |g| + Gem::GemRunner.new.run(["unpack", g.name, "--version", g.version_requirements.to_s]) + mv(Dir.glob("#{g.name}*").first, g.name) + end + + Gem::GemRunner.new.run(["unpack", "rails", "--version", "=#{version}"]) + FileUtils.mv(Dir.glob("rails*").first, "railties") + end + rescue Exception + rm_rf "vendor/rails" + raise + end + end + + desc 'Lock to latest Edge Rails, for a specific release use RELEASE=1.2.0' + task :edge do + require 'open-uri' + version = ENV["RELEASE"] || "edge" + target = "rails_#{version}.zip" + commits = "http://github.com/api/v1/yaml/rails/rails/commits/master" + url = "http://dev.rubyonrails.org/archives/#{target}" + + chdir 'vendor' do + latest_revision = YAML.load(open(commits))["commits"].first["id"] + + puts "Downloading Rails from #{url}" + File.open('rails.zip', 'wb') do |dst| + open url do |src| + while chunk = src.read(4096) + dst << chunk + end + end + end + + puts 'Unpacking Rails' + rm_rf 'rails' + `unzip rails.zip` + %w(rails.zip rails/Rakefile rails/cleanlogs.sh rails/pushgems.rb rails/release.rb).each do |goner| + rm_f goner + end + + touch "rails/REVISION_#{latest_revision}" + end + + puts 'Updating current scripts, javascripts, and configuration settings' + Rake::Task['rails:update'].invoke + end + end + + desc "Unlock this application from freeze of gems or edge and return to a fluid use of system gems" + task :unfreeze do + rm_rf "vendor/rails" + end + + desc "Update both configs, scripts and public/javascripts from Rails" + task :update => [ "update:scripts", "update:javascripts", "update:configs", "update:application_controller" ] + + desc "Applies the template supplied by LOCATION=/path/to/template" + task :template do + require 'rails_generator/generators/applications/app/template_runner' + Rails::TemplateRunner.new(ENV["LOCATION"]) + end + + namespace :update do + desc "Add new scripts to the application script/ directory" + task :scripts do + local_base = "script" + edge_base = "#{File.dirname(__FILE__)}/../../bin" + + local = Dir["#{local_base}/**/*"].reject { |path| File.directory?(path) } + edge = Dir["#{edge_base}/**/*"].reject { |path| File.directory?(path) } + + edge.each do |script| + base_name = script[(edge_base.length+1)..-1] + next if base_name == "rails" + next if local.detect { |path| base_name == path[(local_base.length+1)..-1] } + if !File.directory?("#{local_base}/#{File.dirname(base_name)}") + mkdir_p "#{local_base}/#{File.dirname(base_name)}" + end + install script, "#{local_base}/#{base_name}", :mode => 0755 + end + end + + desc "Update your javascripts from your current rails install" + task :javascripts do + require 'railties_path' + project_dir = RAILS_ROOT + '/public/javascripts/' + scripts = Dir[RAILTIES_PATH + '/html/javascripts/*.js'] + scripts.reject!{|s| File.basename(s) == 'application.js'} if File.exist?(project_dir + 'application.js') + FileUtils.cp(scripts, project_dir) + end + + desc "Update config/boot.rb from your current rails install" + task :configs do + require 'railties_path' + FileUtils.cp(RAILTIES_PATH + '/environments/boot.rb', RAILS_ROOT + '/config/boot.rb') + end + + desc "Rename application.rb to application_controller.rb" + task :application_controller do + old_style = RAILS_ROOT + '/app/controllers/application.rb' + new_style = RAILS_ROOT + '/app/controllers/application_controller.rb' + if File.exists?(old_style) && !File.exists?(new_style) + FileUtils.mv(old_style, new_style) + puts "#{old_style} has been renamed to #{new_style}, update your SCM as necessary" + end + end + + desc "Generate dispatcher files in RAILS_ROOT/public" + task :generate_dispatchers do + require 'railties_path' + FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.rb') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.cgi') + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/gems.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/gems.rake new file mode 100644 index 00000000000..f1c34c7ccaf --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/gems.rake @@ -0,0 +1,78 @@ +desc "List the gems that this rails application depends on" +task :gems => 'gems:base' do + Rails.configuration.gems.each do |gem| + print_gem_status(gem) + end + puts + puts "I = Installed" + puts "F = Frozen" + puts "R = Framework (loaded before rails starts)" +end + +namespace :gems do + task :base do + $gems_rake_task = true + require 'rubygems' + require 'rubygems/gem_runner' + Rake::Task[:environment].invoke + end + + desc "Build any native extensions for unpacked gems" + task :build do + $gems_build_rake_task = true + frozen_gems.each { |gem| gem.build } + end + + namespace :build do + desc "Force the build of all gems" + task :force do + $gems_build_rake_task = true + frozen_gems.each { |gem| gem.build(:force => true) } + end + end + + desc "Installs all required gems." + task :install => :base do + current_gems.each { |gem| gem.install } + end + + desc "Unpacks all required gems into vendor/gems." + task :unpack => :install do + current_gems.each { |gem| gem.unpack } + end + + namespace :unpack do + desc "Unpacks all required gems and their dependencies into vendor/gems." + task :dependencies => :install do + current_gems.each { |gem| gem.unpack(:recursive => true) } + end + end + + desc "Regenerate gem specifications in correct format." + task :refresh_specs do + frozen_gems(false).each { |gem| gem.refresh } + end +end + +def current_gems + gems = Rails.configuration.gems + gems = gems.select { |gem| gem.name == ENV['GEM'] } unless ENV['GEM'].blank? + gems +end + +def frozen_gems(load_specs=true) + Dir[File.join(RAILS_ROOT, 'vendor', 'gems', '*-*')].map do |gem_dir| + Rails::GemDependency.from_directory_name(gem_dir, load_specs) + end +end + +def print_gem_status(gem, indent=1) + code = case + when gem.framework_gem? then 'R' + when gem.frozen? then 'F' + when gem.installed? then 'I' + else ' ' + end + puts " "*(indent-1)+" - [#{code}] #{gem.name} #{gem.requirement.to_s}" + gem.dependencies.each { |g| print_gem_status(g, indent+1) } +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/log.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/log.rake new file mode 100644 index 00000000000..6e1334692ee --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/log.rake @@ -0,0 +1,9 @@ +namespace :log do + desc "Truncates all *.log files in log/ to zero bytes" + task :clear do + FileList["log/*.log"].each do |log_file| + f = File.open(log_file, "w") + f.close + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/middleware.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/middleware.rake new file mode 100644 index 00000000000..05f159184e2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/middleware.rake @@ -0,0 +1,7 @@ +desc 'Prints out your Rack middleware stack' +task :middleware => :environment do + ActionController::Dispatcher.middleware.active.each do |middleware| + puts "use #{middleware.inspect}" + end + puts "run ActionController::Dispatcher.new" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/misc.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/misc.rake new file mode 100644 index 00000000000..9e6f96db5b0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/misc.rake @@ -0,0 +1,63 @@ +task :default => :test +task :environment do + $rails_rake_task = true + require(File.join(RAILS_ROOT, 'config', 'environment')) +end + +task :rails_env do + unless defined? RAILS_ENV + RAILS_ENV = ENV['RAILS_ENV'] ||= 'development' + end +end + +desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.' +task :secret do + puts ActiveSupport::SecureRandom.hex(64) +end + +require 'active_support' +namespace :time do + namespace :zones do + desc 'Displays names of all time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6' + task :all do + build_time_zone_list(:all) + end + + desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6' + task :us do + build_time_zone_list(:us_zones) + end + + desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time' + task :local do + jan_offset = Time.now.beginning_of_year.utc_offset + jul_offset = Time.now.beginning_of_year.change(:month => 7).utc_offset + offset = jan_offset < jul_offset ? jan_offset : jul_offset + build_time_zone_list(:all, offset) + end + + # to find UTC -06:00 zones, OFFSET can be set to either -6, -6:00 or 21600 + def build_time_zone_list(method, offset = ENV['OFFSET']) + if offset + offset = if offset.to_s.match(/(\+|-)?(\d+):(\d+)/) + sign = $1 == '-' ? -1 : 1 + hours, minutes = $2.to_f, $3.to_f + ((hours * 3600) + (minutes.to_f * 60)) * sign + elsif offset.to_f.abs <= 13 + offset.to_f * 3600 + else + offset.to_f + end + end + previous_offset = nil + ActiveSupport::TimeZone.__send__(method).each do |zone| + if offset.nil? || offset == zone.utc_offset + puts "\n* UTC #{zone.formatted_offset} *" unless zone.utc_offset == previous_offset + puts zone.name + previous_offset = zone.utc_offset + end + end + puts "\n" + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/rails.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/rails.rb new file mode 100644 index 00000000000..0600a05a543 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/rails.rb @@ -0,0 +1,9 @@ +$VERBOSE = nil + +# Load Rails rakefile extensions +Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext } + +# Load any custom rakefile extensions +Dir["#{RAILS_ROOT}/vendor/plugins/*/tasks/**/*.rake"].sort.each { |ext| load ext } +Dir["#{RAILS_ROOT}/vendor/plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext } +Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/routes.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/routes.rake new file mode 100644 index 00000000000..abbf3258c18 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/routes.rake @@ -0,0 +1,18 @@ +desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.' +task :routes => :environment do + all_routes = ENV['CONTROLLER'] ? ActionController::Routing::Routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : ActionController::Routing::Routes.routes + routes = all_routes.collect do |route| + name = ActionController::Routing::Routes.named_routes.routes.index(route).to_s + verb = route.conditions[:method].to_s.upcase + segs = route.segments.inject("") { |str,s| str << s.to_s } + segs.chop! if segs.length > 1 + reqs = route.requirements.empty? ? "" : route.requirements.inspect + {:name => name, :verb => verb, :segs => segs, :reqs => reqs} + end + name_width = routes.collect {|r| r[:name]}.collect {|n| n.length}.max + verb_width = routes.collect {|r| r[:verb]}.collect {|v| v.length}.max + segs_width = routes.collect {|r| r[:segs]}.collect {|s| s.length}.max + routes.each do |r| + puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:segs].ljust(segs_width)} #{r[:reqs]}" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/statistics.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/statistics.rake new file mode 100644 index 00000000000..5ab27a0f625 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/statistics.rake @@ -0,0 +1,17 @@ +STATS_DIRECTORIES = [ + %w(Controllers app/controllers), + %w(Helpers app/helpers), + %w(Models app/models), + %w(Libraries lib/), + %w(APIs app/apis), + %w(Integration\ tests test/integration), + %w(Functional\ tests test/functional), + %w(Unit\ tests test/unit) + +].collect { |name, dir| [ name, "#{RAILS_ROOT}/#{dir}" ] }.select { |name, dir| File.directory?(dir) } + +desc "Report code statistics (KLOCs, etc) from the application" +task :stats do + require 'code_statistics' + CodeStatistics.new(*STATS_DIRECTORIES).to_s +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/testing.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/testing.rake new file mode 100644 index 00000000000..fd5e52a05bc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/testing.rake @@ -0,0 +1,139 @@ +TEST_CHANGES_SINCE = Time.now - 600 + +# Look up tests for recently modified sources. +def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago) + FileList[source_pattern].map do |path| + if File.mtime(path) > touched_since + tests = [] + source_dir = File.dirname(path).split("/") + source_file = File.basename(path, '.rb') + + # Support subdirs in app/models and app/controllers + modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path + + # For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb + test = "#{modified_test_path}/#{source_file}_test.rb" + tests.push test if File.exist?(test) + + # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb + test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}" + FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exist?(test) + + return tests + + end + end.flatten.compact +end + + +# Recreated here from ActiveSupport because :uncommitted needs it before Rails is available +module Kernel + def silence_stderr + old_stderr = STDERR.dup + STDERR.reopen(RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'NUL:' : '/dev/null') + STDERR.sync = true + yield + ensure + STDERR.reopen(old_stderr) + end +end + +desc 'Run all unit, functional and integration tests' +task :test do + errors = %w(test:units test:functionals test:integration).collect do |task| + begin + Rake::Task[task].invoke + nil + rescue => e + task + end + end.compact + abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any? +end + +namespace :test do + Rake::TestTask.new(:recent => "db:test:prepare") do |t| + since = TEST_CHANGES_SINCE + touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } + + recent_tests('app/models/**/*.rb', 'test/unit', since) + + recent_tests('app/controllers/**/*.rb', 'test/functional', since) + + t.libs << 'test' + t.verbose = true + t.test_files = touched.uniq + end + Rake::Task['test:recent'].comment = "Test recent changes" + + Rake::TestTask.new(:uncommitted => "db:test:prepare") do |t| + def t.file_list + if File.directory?(".svn") + changed_since_checkin = silence_stderr { `svn status` }.map { |path| path.chomp[7 .. -1] } + elsif File.directory?(".git") + changed_since_checkin = silence_stderr { `git ls-files --modified --others` }.map { |path| path.chomp } + else + abort "Not a Subversion or Git checkout." + end + + models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ } + controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ } + + unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" } + functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" } + + unit_tests.uniq + functional_tests.uniq + end + + t.libs << 'test' + t.verbose = true + end + Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)" + + Rake::TestTask.new(:units => "db:test:prepare") do |t| + t.libs << "test" + t.pattern = 'test/unit/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:units'].comment = "Run the unit tests in test/unit" + + Rake::TestTask.new(:functionals => "db:test:prepare") do |t| + t.libs << "test" + t.pattern = 'test/functional/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional" + + Rake::TestTask.new(:integration => "db:test:prepare") do |t| + t.libs << "test" + t.pattern = 'test/integration/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:integration'].comment = "Run the integration tests in test/integration" + + Rake::TestTask.new(:benchmark => 'db:test:prepare') do |t| + t.libs << 'test' + t.pattern = 'test/performance/**/*_test.rb' + t.verbose = true + t.options = '-- --benchmark' + end + Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests' + + Rake::TestTask.new(:profile => 'db:test:prepare') do |t| + t.libs << 'test' + t.pattern = 'test/performance/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:profile'].comment = 'Profile the performance tests' + + Rake::TestTask.new(:plugins => :environment) do |t| + t.libs << "test" + + if ENV['PLUGIN'] + t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb" + else + t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb' + end + + t.verbose = true + end + Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/tmp.rake b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/tmp.rake new file mode 100644 index 00000000000..fea15058bb8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/tasks/tmp.rake @@ -0,0 +1,37 @@ +namespace :tmp do + desc "Clear session, cache, and socket files from tmp/" + task :clear => [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"] + + desc "Creates tmp directories for sessions, cache, sockets, and pids" + task :create do + FileUtils.mkdir_p(%w( tmp/sessions tmp/cache tmp/sockets tmp/pids )) + end + + namespace :sessions do + desc "Clears all files in tmp/sessions" + task :clear do + FileUtils.rm(Dir['tmp/sessions/[^.]*']) + end + end + + namespace :cache do + desc "Clears all files and directories in tmp/cache" + task :clear do + FileUtils.rm_rf(Dir['tmp/cache/[^.]*']) + end + end + + namespace :sockets do + desc "Clears all files in tmp/sockets" + task :clear do + FileUtils.rm(Dir['tmp/sockets/[^.]*']) + end + end + + namespace :pids do + desc "Clears all files in tmp/pids" + task :clear do + FileUtils.rm(Dir['tmp/pids/[^.]*']) + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/test_help.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/test_help.rb new file mode 100644 index 00000000000..f8608925d55 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/test_help.rb @@ -0,0 +1,38 @@ +# Make double-sure the RAILS_ENV is set to test, +# so fixtures are loaded to the right database +silence_warnings { RAILS_ENV = "test" } + +require 'test/unit' +require 'action_controller/test_case' +require 'action_view/test_case' +require 'action_controller/integration' +require 'action_mailer/test_case' if defined?(ActionMailer) + +if defined?(ActiveRecord) + require 'active_record/test_case' + require 'active_record/fixtures' + + class ActiveSupport::TestCase + include ActiveRecord::TestFixtures + self.fixture_path = "#{RAILS_ROOT}/test/fixtures/" + self.use_instantiated_fixtures = false + self.use_transactional_fixtures = true + end + + ActionController::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path + + def create_fixtures(*table_names, &block) + Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block) + end +end + +begin + require_library_or_gem 'ruby-debug' + Debugger.start + if Debugger.respond_to?(:settings) + Debugger.settings[:autoeval] = true + Debugger.settings[:autolist] = 1 + end +rescue LoadError + # ruby-debug wasn't available so neither can the debugging be +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/webrick_server.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/webrick_server.rb new file mode 100644 index 00000000000..2f60151b222 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/lib/webrick_server.rb @@ -0,0 +1,156 @@ +# Donated by Florian Gross + +require 'webrick' +require 'cgi' +require 'stringio' +require 'dispatcher' + +include WEBrick + +class CGI #:nodoc: + def stdinput + @stdin || $stdin + end + + def env_table + @env_table || ENV + end + + def initialize(type = "query", table = nil, stdin = nil) + @env_table, @stdin = table, stdin + + if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE") + Apache.request.setup_cgi_env + end + + extend QueryExtension + @multipart = false + if defined?(CGI_PARAMS) + warn "do not use CGI_PARAMS and CGI_COOKIES" + @params = CGI_PARAMS.dup + @cookies = CGI_COOKIES.dup + else + initialize_query() # set @params, @cookies + end + @output_cookies = nil + @output_hidden = nil + end +end + +# A custom dispatch servlet for use with WEBrick. It dispatches requests +# (using the Rails Dispatcher) to the appropriate controller/action. By default, +# it restricts WEBrick to a managing a single Rails request at a time, but you +# can change this behavior by setting ActionController::Base.allow_concurrency +# to true. +class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet + # Start the WEBrick server with the given options, mounting the + # DispatchServlet at /. + def self.dispatch(options = {}) + Socket.do_not_reverse_lookup = true # patch for OS X + + params = { :Port => options[:port].to_i, + :ServerType => options[:server_type], + :BindAddress => options[:ip] } + params[:MimeTypes] = options[:mime_types] if options[:mime_types] + + server = WEBrick::HTTPServer.new(params) + server.mount('/', DispatchServlet, options) + + trap("INT") { server.shutdown } + server.start + end + + def initialize(server, options) #:nodoc: + @server_options = options + @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root]) + # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/") + # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb + Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory']) + super + end + + def service(req, res) #:nodoc: + unless handle_file(req, res) + unless handle_dispatch(req, res) + raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + end + + def handle_file(req, res) #:nodoc: + begin + req = req.dup + path = req.path.dup + + # Add .html if the last path piece has no . in it + path << '.html' if path != '/' && (%r{(^|/)[^./]+$} =~ path) + path.gsub!('+', ' ') # Unescape + since FileHandler doesn't do so. + + req.instance_variable_set(:@path_info, path) # Set the modified path... + + @file_handler.send(:service, req, res) + return true + rescue HTTPStatus::PartialContent, HTTPStatus::NotModified => err + res.set_error(err) + return true + rescue => err + return false + end + end + + def handle_dispatch(req, res, origin = nil) #:nodoc: + data = StringIO.new + Dispatcher.dispatch( + CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")), + ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, + data + ) + + header, body = extract_header_and_body(data) + + set_charset(header) + assign_status(res, header) + res.cookies.concat(header.delete('set-cookie') || []) + header.each { |key, val| res[key] = val.join(", ") } + + res.body = body + return true + rescue => err + p err, err.backtrace + return false + end + + private + def create_env_table(req, origin) + env = req.meta_vars.clone + env.delete "SCRIPT_NAME" + env["QUERY_STRING"] = req.request_uri.query + env["REQUEST_URI"] = origin if origin + return env + end + + def extract_header_and_body(data) + data.rewind + data = data.read + + raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2) + header = WEBrick::HTTPUtils::parse_header(raw_header) + + return header, body + end + + def set_charset(header) + ct = header["content-type"] + if ct.any? { |x| x =~ /^text\// } && ! ct.any? { |x| x =~ /charset=/ } + ch = @server_options[:charset] || "UTF-8" + ct.find { |x| x =~ /^text\// } << ("; charset=" + ch) + end + end + + def assign_status(res, header) + if /^(\d+)/ =~ header['status'][0] + res.status = $1.to_i + header.delete('status') + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/abstract_unit.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/abstract_unit.rb new file mode 100644 index 00000000000..85f680da822 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/abstract_unit.rb @@ -0,0 +1,27 @@ +$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" +$:.unshift File.dirname(__FILE__) + "/../../activerecord/lib" +$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" +$:.unshift File.dirname(__FILE__) + "/../../actionmailer/lib" +$:.unshift File.dirname(__FILE__) + "/../lib" +$:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" + +require 'stringio' +require 'rubygems' +require 'test/unit' + +require 'active_support' +require 'active_support/test_case' + +if defined?(RAILS_ROOT) + RAILS_ROOT.replace File.dirname(__FILE__) +else + RAILS_ROOT = File.dirname(__FILE__) +end + +def uses_gem(gem_name, test_name, version = '> 0') + gem gem_name.to_s, version + require gem_name.to_s + yield +rescue LoadError + $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/backtrace_cleaner_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/backtrace_cleaner_test.rb new file mode 100644 index 00000000000..6319aeeab05 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/backtrace_cleaner_test.rb @@ -0,0 +1,61 @@ +require 'abstract_unit' + +require 'initializer' +require 'rails/backtrace_cleaner' + +if defined? Test::Unit::Util::BacktraceFilter + class TestWithBacktrace + include Test::Unit::Util::BacktraceFilter + include Rails::BacktraceFilterForTestUnit + end + + class BacktraceCleanerFilterTest < ActiveSupport::TestCase + def setup + @test = TestWithBacktrace.new + @backtrace = [ './test/rails/benchmark_test.rb', './test/rails/dependencies.rb', '/opt/local/lib/ruby/kernel.rb' ] + end + + test "test with backtrace should use the rails backtrace cleaner to clean" do + Rails.stubs(:backtrace_cleaner).returns(stub(:clean)) + Rails.backtrace_cleaner.expects(:clean).with(@backtrace, nil) + @test.send(:filter_backtrace, @backtrace) + end + + test "filter backtrace should have the same arity as Test::Unit::Util::BacktraceFilter" do + assert_nothing_raised do + @test.send(:filter_backtrace, @backtrace, '/opt/local/lib') + end + end + end +else + $stderr.puts 'No BacktraceFilter for minitest' +end + +class BacktraceCleanerVendorGemTest < ActiveSupport::TestCase + def setup + @cleaner = Rails::BacktraceCleaner.new + end + + test "should format installed gems correctly" do + @backtrace = [ "#{Gem.path[0]}/gems/nosuchgem-1.2.3/lib/foo.rb" ] + @result = @cleaner.clean(@backtrace) + assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0] + end + + test "should format installed gems not in Gem.default_dir correctly" do + @target_dir = Gem.path.detect { |p| p != Gem.default_dir } + # skip this test if default_dir is the only directory on Gem.path + if @target_dir + @backtrace = [ "#{@target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ] + @result = @cleaner.clean(@backtrace) + assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0] + end + end + + test "should format vendor gems correctly" do + @backtrace = [ "#{Rails::GemDependency.unpacked_path}/nosuchgem-1.2.3/lib/foo.rb" ] + @result = @cleaner.clean(@backtrace) + assert_equal "nosuchgem (1.2.3) [v] lib/foo.rb", @result[0] + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/boot_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/boot_test.rb new file mode 100644 index 00000000000..08fcc82e6f4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/boot_test.rb @@ -0,0 +1,178 @@ +require 'abstract_unit' +require 'initializer' +require "#{File.dirname(__FILE__)}/../environments/boot" + +class BootTest < Test::Unit::TestCase + def test_boot_returns_if_booted + Rails.expects(:booted?).returns(true) + Rails.expects(:pick_boot).never + assert_nil Rails.boot! + end + + def test_boot_preinitializes_then_picks_and_runs_if_not_booted + Rails.expects(:booted?).returns(false) + Rails.expects(:preinitialize) + Rails.expects(:pick_boot).returns(mock(:run => 'result')) + assert_equal 'result', Rails.boot! + end + + def test_preinitialize_does_not_raise_exception_if_preinitializer_file_does_not_exist + Rails.stubs(:preinitializer_path).returns('/there/is/no/such/file') + + assert_nothing_raised { Rails.preinitialize } + end + + def test_load_preinitializer_loads_preinitializer_file + Rails.stubs(:preinitializer_path).returns("#{File.dirname(__FILE__)}/fixtures/environment_with_constant.rb") + + assert_nil $initialize_test_set_from_env + Rails.preinitialize + assert_equal "success", $initialize_test_set_from_env + ensure + $initialize_test_set_from_env = nil + end + + def test_boot_vendor_rails_by_default + Rails.expects(:booted?).returns(false) + Rails.expects(:preinitialize) + File.expects(:exist?).with("#{RAILS_ROOT}/vendor/rails").returns(true) + Rails::VendorBoot.any_instance.expects(:run).returns('result') + assert_equal 'result', Rails.boot! + end + + def test_boot_gem_rails_otherwise + Rails.expects(:booted?).returns(false) + Rails.expects(:preinitialize) + File.expects(:exist?).with("#{RAILS_ROOT}/vendor/rails").returns(false) + Rails::GemBoot.any_instance.expects(:run).returns('result') + assert_equal 'result', Rails.boot! + end + + def test_run_loads_initializer_and_sets_load_path + boot = Rails::Boot.new + boot.expects(:load_initializer) + Rails::Initializer.expects(:run).with(:set_load_path) + boot.run + end +end + +class VendorBootTest < Test::Unit::TestCase + include Rails + + def test_load_initializer_requires_from_vendor_rails + boot = VendorBoot.new + boot.expects(:require).with("#{RAILS_ROOT}/vendor/rails/railties/lib/initializer") + Rails::Initializer.expects(:run).with(:install_gem_spec_stubs) + Rails::GemDependency.expects(:add_frozen_gem_path) + boot.load_initializer + end +end + +class GemBootTest < Test::Unit::TestCase + include Rails + + def test_load_initializer_loads_rubygems_and_the_rails_gem + boot = GemBoot.new + GemBoot.expects(:load_rubygems) + boot.expects(:load_rails_gem) + boot.expects(:require).with('initializer') + boot.load_initializer + end + + def test_load_rubygems_exits_with_error_if_missing + GemBoot.expects(:require).with('rubygems').raises(LoadError, 'missing rubygems') + STDERR.expects(:puts) + GemBoot.expects(:exit).with(1) + GemBoot.load_rubygems + end + + def test_load_rubygems_exits_with_error_if_too_old + GemBoot.stubs(:rubygems_version).returns('0.0.1') + GemBoot.expects(:require).with('rubygems').returns(true) + STDERR.expects(:puts) + GemBoot.expects(:exit).with(1) + GemBoot.load_rubygems + end + + def test_load_rails_gem_activates_specific_gem_if_version_given + GemBoot.stubs(:gem_version).returns('0.0.1') + + boot = GemBoot.new + boot.expects(:gem).with('rails', '0.0.1') + boot.load_rails_gem + end + + def test_load_rails_gem_activates_latest_gem_if_no_version_given + GemBoot.stubs(:gem_version).returns(nil) + + boot = GemBoot.new + boot.expects(:gem).with('rails') + boot.load_rails_gem + end + + def test_load_rails_gem_exits_with_error_if_missing + GemBoot.stubs(:gem_version).returns('0.0.1') + + boot = GemBoot.new + boot.expects(:gem).with('rails', '0.0.1').raises(Gem::LoadError, 'missing rails 0.0.1 gem') + STDERR.expects(:puts) + boot.expects(:exit).with(1) + boot.load_rails_gem + end +end + +class ParseGemVersionTest < Test::Unit::TestCase + def test_should_return_nil_if_no_lines_are_passed + assert_equal nil, parse('') + assert_equal nil, parse(nil) + end + + def test_should_accept_either_single_or_double_quotes + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse('RAILS_GEM_VERSION = "1.2.3"') + end + + def test_should_return_nil_if_no_lines_match + assert_equal nil, parse('nothing matches on this line\nor on this line') + end + + def test_should_parse_with_no_leading_space + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION") + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3'") + end + + def test_should_parse_with_any_number_of_leading_spaces + assert_equal nil, parse([]) + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION") + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION") + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3'") + end + + def test_should_ignore_unrelated_comments + assert_equal "1.2.3", parse("# comment\nRAILS_GEM_VERSION = '1.2.3'\n# comment") + end + + def test_should_ignore_commented_version_lines + assert_equal "1.2.3", parse("#RAILS_GEM_VERSION = '9.8.7'\nRAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse("# RAILS_GEM_VERSION = '9.8.7'\nRAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3'\n# RAILS_GEM_VERSION = '9.8.7'") + end + + def test_should_allow_advanced_rubygems_version_specifications + # See http://rubygems.org/read/chapter/16 + assert_equal "=1.2.3", parse("RAILS_GEM_VERSION = '=1.2.3'") # equal sign + assert_equal "= 1.2.3", parse("RAILS_GEM_VERSION = '= 1.2.3'") # with space + assert_equal "!=1.2.3", parse("RAILS_GEM_VERSION = '!=1.2.3'") # not equal + assert_equal ">1.2.3", parse("RAILS_GEM_VERSION = '>1.2.3'") # greater than + assert_equal "<1.2.3", parse("RAILS_GEM_VERSION = '<1.2.3'") # less than + assert_equal ">=1.2.3", parse("RAILS_GEM_VERSION = '>=1.2.3'") # greater than or equal + assert_equal "<=1.2.3", parse("RAILS_GEM_VERSION = '<=1.2.3'") # less than or equal + assert_equal "~>1.2.3.0", parse("RAILS_GEM_VERSION = '~>1.2.3.0'") # approximately greater than + end + + private + def parse(text) + Rails::GemBoot.parse_gem_version(text) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/console_app_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/console_app_test.rb new file mode 100644 index 00000000000..f11de087e35 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/console_app_test.rb @@ -0,0 +1,42 @@ +require 'abstract_unit' + +require 'action_controller' # console_app uses 'action_controller/integration' + +unless defined? ApplicationController + class ApplicationController < ActionController::Base; end + ActionController::Base.session_store = nil +end + +require 'dispatcher' +require 'console_app' + +# console_app sets Test::Unit.run to work around the at_exit hook in test/unit, which kills IRB +if Test::Unit.respond_to?(:run=) + Test::Unit.run = false + + class ConsoleAppTest < Test::Unit::TestCase + def test_app_method_should_return_integration_session + assert_nothing_thrown do + console_session = app + assert_not_nil console_session + assert_instance_of ActionController::Integration::Session, + console_session + end + end + + def test_reload_should_fire_preparation_callbacks + a = b = c = nil + + Dispatcher.to_prepare { a = b = c = 1 } + Dispatcher.to_prepare { b = c = 2 } + Dispatcher.to_prepare { c = 3 } + ActionController::Routing::Routes.expects(:reload) + + reload! + + assert_equal 1, a + assert_equal 2, b + assert_equal 3, c + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fcgi_dispatcher_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fcgi_dispatcher_test.rb new file mode 100644 index 00000000000..ebbf459ab27 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fcgi_dispatcher_test.rb @@ -0,0 +1,266 @@ +require 'abstract_unit' + +if RUBY_VERSION < '1.9.0' +uses_gem "fcgi", "0.8.7" do + +require 'action_controller' +require 'fcgi_handler' + +Dispatcher.middleware.clear + +class RailsFCGIHandlerTest < Test::Unit::TestCase + def setup + @log = StringIO.new + @handler = RailsFCGIHandler.new(@log) + end + + def test_process_restart + request = mock + FCGI.stubs(:each).yields(request) + + @handler.expects(:process_request).once + @handler.expects(:dispatcher_error).never + + @handler.expects(:when_ready).returns(:restart) + @handler.expects(:close_connection).with(request) + @handler.expects(:reload!).never + @handler.expects(:restart!) + + @handler.process! + end + + def test_process_exit + request = mock + FCGI.stubs(:each).yields(request) + + @handler.expects(:process_request).once + @handler.expects(:dispatcher_error).never + + @handler.expects(:when_ready).returns(:exit) + @handler.expects(:close_connection).with(request) + @handler.expects(:reload!).never + @handler.expects(:restart!).never + + @handler.process! + end + + def test_process_with_system_exit_exception + request = mock + FCGI.stubs(:each).yields(request) + + @handler.expects(:process_request).once.raises(SystemExit) + @handler.stubs(:dispatcher_log) + @handler.expects(:dispatcher_log).with(:info, regexp_matches(/^stopping/)) + @handler.expects(:dispatcher_error).never + + @handler.expects(:when_ready).never + @handler.expects(:close_connection).never + @handler.expects(:reload!).never + @handler.expects(:restart!).never + + @handler.process! + end + + def test_restart_handler_outside_request + @handler.expects(:dispatcher_log).with(:info, "asked to restart ASAP") + @handler.expects(:restart!).once + + @handler.send(:restart_handler, nil) + assert_equal nil, @handler.when_ready + end + + def test_install_signal_handler_should_log_on_bad_signal + @handler.stubs(:trap).raises(ArgumentError) + + @handler.expects(:dispatcher_log).with(:warn, "Ignoring unsupported signal CHEESECAKE.") + @handler.send(:install_signal_handler, "CHEESECAKE", nil) + end + + def test_reload + @handler.expects(:restore!) + @handler.expects(:dispatcher_log).with(:info, "reloaded") + + @handler.send(:reload!) + assert_nil @handler.when_ready + end + + + def test_reload_runs_gc_when_gc_request_period_set + @handler.expects(:run_gc!) + @handler.expects(:restore!) + @handler.expects(:dispatcher_log).with(:info, "reloaded") + @handler.gc_request_period = 10 + @handler.send(:reload!) + end + + def test_reload_doesnt_run_gc_if_gc_request_period_isnt_set + @handler.expects(:run_gc!).never + @handler.expects(:restore!) + @handler.expects(:dispatcher_log).with(:info, "reloaded") + @handler.send(:reload!) + end + + def test_restart! + @handler.expects(:dispatcher_log).with(:info, "restarted") + @handler.expects(:exec).returns('restarted') + assert_equal 'restarted', @handler.send(:restart!) + end + + def test_restore! + $".expects(:replace) + Dispatcher.expects(:reset_application!) + ActionController::Routing::Routes.expects(:reload) + @handler.send(:restore!) + end + + def test_uninterrupted_processing + request = mock + FCGI.expects(:each).yields(request) + @handler.expects(:process_request).with(request) + + @handler.process! + + assert_nil @handler.when_ready + end +end + + +class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase + class ::RailsFCGIHandler + attr_accessor :signal + alias_method :old_gc_countdown, :gc_countdown + def gc_countdown + signal ? Process.kill(signal, $$) : old_gc_countdown + end + end + + def setup + @log = StringIO.new + @handler = RailsFCGIHandler.new(@log) + @dispatcher = mock + Dispatcher.stubs(:new).returns(@dispatcher) + end + + def test_interrupted_via_HUP_when_not_in_request + request = mock + FCGI.expects(:each).once.yields(request) + @handler.expects(:signal).times(2).returns('HUP') + + @handler.expects(:reload!).once + @handler.expects(:close_connection).never + @handler.expects(:exit).never + + @handler.process! + assert_equal :reload, @handler.when_ready + end + + def test_interrupted_via_USR1_when_not_in_request + request = mock + FCGI.expects(:each).once.yields(request) + @handler.expects(:signal).times(2).returns('USR1') + @handler.expects(:exit_handler).never + + @handler.expects(:reload!).never + @handler.expects(:close_connection).with(request).once + @handler.expects(:exit).never + + @handler.process! + assert_nil @handler.when_ready + end + + def test_restart_via_USR2_when_in_request + request = mock + FCGI.expects(:each).once.yields(request) + @handler.expects(:signal).times(2).returns('USR2') + @handler.expects(:exit_handler).never + + @handler.expects(:reload!).never + @handler.expects(:close_connection).with(request).once + @handler.expects(:exit).never + @handler.expects(:restart!).once + + @handler.process! + assert_equal :restart, @handler.when_ready + end + + def test_interrupted_via_TERM + request = mock + FCGI.expects(:each).once.yields(request) + ::Rack::Handler::FastCGI.expects(:serve).once.returns('TERM') + + @handler.expects(:reload!).never + @handler.expects(:close_connection).never + + @handler.process! + assert_nil @handler.when_ready + end + + def test_runtime_exception_in_fcgi + error = RuntimeError.new('foo') + FCGI.expects(:each).times(2).raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^retrying/)) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) + @handler.process! + end + + def test_runtime_error_in_dispatcher + request = mock + error = RuntimeError.new('foo') + FCGI.expects(:each).once.yields(request) + ::Rack::Handler::FastCGI.expects(:serve).once.raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/)) + @handler.process! + end + + def test_signal_exception_in_fcgi + error = SignalException.new('USR2') + FCGI.expects(:each).once.raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) + @handler.process! + end + + def test_signal_exception_in_dispatcher + request = mock + error = SignalException.new('USR2') + FCGI.expects(:each).once.yields(request) + ::Rack::Handler::FastCGI.expects(:serve).once.raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) + @handler.process! + end +end + + +class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase + def setup + @log = StringIO.new + end + + def teardown + GC.enable + end + + def test_normal_gc + @handler = RailsFCGIHandler.new(@log) + assert_nil @handler.gc_request_period + + # When GC is enabled, GC.disable disables and returns false. + assert_equal false, GC.disable + end + + def test_periodic_gc + @handler = RailsFCGIHandler.new(@log, 10) + assert_equal 10, @handler.gc_request_period + + request = mock + FCGI.expects(:each).times(10).yields(request) + + @handler.expects(:run_gc!).never + 9.times { @handler.process! } + @handler.expects(:run_gc!).once + @handler.process! + + assert_nil @handler.when_ready + end +end +end # uses_gem "fcgi" +end # exclude 1.9 diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml new file mode 100644 index 00000000000..fe80872a168 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml @@ -0,0 +1 @@ +# an empty YAML file - any content in here seems to get parsed as a string \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb new file mode 100644 index 00000000000..d4262f89716 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb @@ -0,0 +1 @@ +# intentionally empty \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb new file mode 100644 index 00000000000..d4262f89716 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb @@ -0,0 +1 @@ +# intentionally empty \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/eager/zoo.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/eager/zoo.rb new file mode 100644 index 00000000000..8b10ef984ba --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/eager/zoo.rb @@ -0,0 +1,3 @@ +class Zoo + include ReptileHouse +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/eager/zoo/reptile_house.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/eager/zoo/reptile_house.rb new file mode 100644 index 00000000000..82bbafce797 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/eager/zoo/reptile_house.rb @@ -0,0 +1,2 @@ +module Zoo::ReptileHouse +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/environment_with_constant.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/environment_with_constant.rb new file mode 100644 index 00000000000..23e1f7afd91 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/environment_with_constant.rb @@ -0,0 +1 @@ +$initialize_test_set_from_env = 'success' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/lib/generators/working/working_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/lib/generators/working/working_generator.rb new file mode 100644 index 00000000000..465b34319ae --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/lib/generators/working/working_generator.rb @@ -0,0 +1,2 @@ +class WorkingGenerator < Rails::Generator::NamedBase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb new file mode 100644 index 00000000000..2d373ce4220 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb @@ -0,0 +1,5 @@ +class MetalA < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb new file mode 100644 index 00000000000..a8bbf3fd607 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb @@ -0,0 +1,5 @@ +class MetalB < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/pluralmetal/app/metal/legacy_routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/pluralmetal/app/metal/legacy_routes.rb new file mode 100644 index 00000000000..0cd3737c321 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/pluralmetal/app/metal/legacy_routes.rb @@ -0,0 +1,5 @@ +class LegacyRoutes < Rails::Rack::Metal + def self.call(env) + [301, { "Location" => "http://example.com"}, []] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/singlemetal/app/metal/foo_metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/singlemetal/app/metal/foo_metal.rb new file mode 100644 index 00000000000..5f5b0875928 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/singlemetal/app/metal/foo_metal.rb @@ -0,0 +1,5 @@ +class FooMetal < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_a.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_a.rb new file mode 100644 index 00000000000..25b3bb0abc6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_a.rb @@ -0,0 +1,7 @@ +module Folder + class MetalA < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_b.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_b.rb new file mode 100644 index 00000000000..7583363f714 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_b.rb @@ -0,0 +1,7 @@ +module Folder + class MetalB < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/alternate/a/generators/a_generator/a_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/alternate/a/generators/a_generator/a_generator.rb new file mode 100644 index 00000000000..b33f2dad181 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/alternate/a/generators/a_generator/a_generator.rb @@ -0,0 +1,4 @@ +class AGenerator < Rails::Generator::Base + def manifest + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/init.rb new file mode 100644 index 00000000000..6a771b5b685 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/init.rb @@ -0,0 +1 @@ +raise 'This init.rb should not be evaluated because rails/init.rb exists' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/lib/gemlike.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/lib/gemlike.rb new file mode 100644 index 00000000000..2088103e459 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/lib/gemlike.rb @@ -0,0 +1,2 @@ +module Gemlike +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/rails/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/rails/init.rb new file mode 100644 index 00000000000..171a293eb3b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/gemlike/rails/init.rb @@ -0,0 +1,7 @@ +# I have access to my directory and the Rails config. +raise 'directory expected but undefined in init.rb' unless defined? directory +raise 'config expected but undefined in init.rb' unless defined? config + +# My lib/ dir must be in the load path. +require 'gemlike' +raise 'missing mixin from my lib/ dir' unless defined? Gemlike diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/plugin_with_no_lib_dir/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/plugin_with_no_lib_dir/init.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/about.yml b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/about.yml new file mode 100644 index 00000000000..d85a7cc0e3f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/about.yml @@ -0,0 +1,2 @@ +author: Plugin Author +version: 1.0.0 \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb new file mode 100644 index 00000000000..8fda8197d19 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb @@ -0,0 +1,4 @@ +class StubbyGenerator < Rails::Generator::Base + def manifest + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/init.rb new file mode 100644 index 00000000000..81beeb0d322 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/init.rb @@ -0,0 +1,7 @@ +# I have access to my directory and the Rails config. +raise 'directory expected but undefined in init.rb' unless defined? directory +raise 'config expected but undefined in init.rb' unless defined? config + +# My lib/ dir must be in the load path. +require 'stubby_mixin' +raise 'missing mixin from my lib/ dir' unless defined? StubbyMixin diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb new file mode 100644 index 00000000000..2d569e5002a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb @@ -0,0 +1,2 @@ +module StubbyMixin +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/controllers/engine_controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/controllers/engine_controller.rb new file mode 100644 index 00000000000..323ee1c4dcc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/controllers/engine_controller.rb @@ -0,0 +1,2 @@ +class EngineController +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb new file mode 100644 index 00000000000..d67a127ca78 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb @@ -0,0 +1,10 @@ +class EngineMetal + def self.call(env) + if env["PATH_INFO"] =~ /^\/metal/ + [200, {"Content-Type" => "text/html"}, ["Engine metal"]] + else + [404, {"Content-Type" => "text/html"}, ["Not Found"]] + end + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/models/engine_model.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/models/engine_model.rb new file mode 100644 index 00000000000..e265712185e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/app/models/engine_model.rb @@ -0,0 +1,2 @@ +class EngineModel +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml new file mode 100644 index 00000000000..641a7e035c1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml @@ -0,0 +1,2 @@ +en: + hello: "Hello from Engine" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/config/routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/config/routes.rb new file mode 100644 index 00000000000..cca8d1b1466 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/config/routes.rb @@ -0,0 +1,3 @@ +ActionController::Routing::Routes.draw do |map| + map.connect '/engine', :controller => "engine" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/init.rb new file mode 100644 index 00000000000..64e9ae6c309 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/plugins/engines/engine/init.rb @@ -0,0 +1,3 @@ +# My app/models dir must be in the load path. +require 'engine_model' +raise LoadError, 'missing model from my app/models dir' unless defined?(EngineModel) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/foo/bar.html b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/foo/bar.html new file mode 100644 index 00000000000..9a35646205e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/foo/bar.html @@ -0,0 +1 @@ +/foo/bar.html \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/foo/index.html b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/foo/index.html new file mode 100644 index 00000000000..497a2e898f0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/foo/index.html @@ -0,0 +1 @@ +/foo/index.html \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/index.html b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/index.html new file mode 100644 index 00000000000..525950ba6b8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/fixtures/public/index.html @@ -0,0 +1 @@ +/index.html \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/gem_dependency_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/gem_dependency_test.rb new file mode 100644 index 00000000000..a4cf6f7661b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/gem_dependency_test.rb @@ -0,0 +1,219 @@ +require 'plugin_test_helper' + +class Rails::GemDependency + public :install_command, :unpack_command +end + +Rails::VendorGemSourceIndex.silence_spec_warnings = true + +class GemDependencyTest < Test::Unit::TestCase + def setup + @gem = Rails::GemDependency.new "xhpricotx" + @gem_with_source = Rails::GemDependency.new "xhpricotx", :source => "http://code.whytheluckystiff.net" + @gem_with_version = Rails::GemDependency.new "xhpricotx", :version => "= 0.6" + @gem_with_lib = Rails::GemDependency.new "xaws-s3x", :lib => "aws/s3" + @gem_without_load = Rails::GemDependency.new "xhpricotx", :lib => false + end + + def test_configuration_adds_gem_dependency + config = Rails::Configuration.new + config.gem "xaws-s3x", :lib => "aws/s3", :version => "0.4.0" + assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect { |g| g.install_command } + end + + def test_gem_creates_install_command + assert_equal %w(install xhpricotx), @gem.install_command + end + + def test_gem_with_source_creates_install_command + assert_equal %w(install xhpricotx --source http://code.whytheluckystiff.net), @gem_with_source.install_command + end + + def test_gem_with_version_creates_install_command + assert_equal ["install", "xhpricotx", "--version", '"= 0.6"'], @gem_with_version.install_command + end + + def test_gem_creates_unpack_command + assert_equal %w(unpack xhpricotx), @gem.unpack_command + end + + def test_gem_with_version_unpack_install_command + # stub out specification method, or else test will fail if hpricot 0.6 isn't installed + mock_spec = mock() + mock_spec.stubs(:version).returns('0.6') + @gem_with_version.stubs(:specification).returns(mock_spec) + assert_equal ["unpack", "xhpricotx", "--version", '= 0.6'], @gem_with_version.unpack_command + end + + def test_gem_adds_load_paths + @gem.expects(:gem).with(@gem) + @gem.add_load_paths + end + + def test_gem_with_version_adds_load_paths + @gem_with_version.expects(:gem).with(@gem_with_version) + @gem_with_version.add_load_paths + assert @gem_with_version.load_paths_added? + end + + def test_gem_loading + @gem.expects(:gem).with(@gem) + @gem.expects(:require).with(@gem.name) + @gem.add_load_paths + @gem.load + assert @gem.loaded? + end + + def test_gem_with_lib_loading + @gem_with_lib.expects(:gem).with(@gem_with_lib) + @gem_with_lib.expects(:require).with(@gem_with_lib.lib) + @gem_with_lib.add_load_paths + @gem_with_lib.load + assert @gem_with_lib.loaded? + end + + def test_gem_without_lib_loading + @gem_without_load.expects(:gem).with(@gem_without_load) + @gem_without_load.expects(:require).with(@gem_without_load.lib).never + @gem_without_load.add_load_paths + @gem_without_load.load + end + + def test_gem_dependencies_compare_for_uniq + gem1 = Rails::GemDependency.new "gem1" + gem1a = Rails::GemDependency.new "gem1" + gem2 = Rails::GemDependency.new "gem2" + gem2a = Rails::GemDependency.new "gem2" + gem3 = Rails::GemDependency.new "gem2", :version => ">=0.1" + gem3a = Rails::GemDependency.new "gem2", :version => ">=0.1" + gem4 = Rails::GemDependency.new "gem3" + gems = [gem1, gem2, gem1a, gem3, gem2a, gem4, gem3a, gem2, gem4] + assert_equal 4, gems.uniq.size + end + + def test_gem_load_frozen + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_A_VERSION + end + + def test_gem_load_frozen_specific_version + dummy_gem = Rails::GemDependency.new "dummy-gem-b", :version => '0.4.0' + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_B_VERSION + assert_equal '0.4.0', DUMMY_GEM_B_VERSION + end + + def test_gem_load_frozen_minimum_version + dummy_gem = Rails::GemDependency.new "dummy-gem-c", :version => '>=0.5.0' + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_C_VERSION + assert_equal '0.6.0', DUMMY_GEM_C_VERSION + end + + def test_gem_load_missing_specification + dummy_gem = Rails::GemDependency.new "dummy-gem-d" + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_D_VERSION + assert_equal '1.0.0', DUMMY_GEM_D_VERSION + assert_equal ['lib', 'lib/dummy-gem-d.rb'], dummy_gem.specification.files + end + + def test_gem_load_bad_specification + dummy_gem = Rails::GemDependency.new "dummy-gem-e", :version => "= 1.0.0" + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_E_VERSION + assert_equal '1.0.0', DUMMY_GEM_E_VERSION + end + + def test_gem_handle_missing_dependencies + dummy_gem = Rails::GemDependency.new "dummy-gem-g" + dummy_gem.add_load_paths + dummy_gem.load + assert_equal 1, dummy_gem.dependencies.size + assert_equal 1, dummy_gem.dependencies.first.dependencies.size + assert_nothing_raised do + dummy_gem.dependencies.each do |g| + g.dependencies + end + end + end + + def test_gem_ignores_development_dependencies + dummy_gem = Rails::GemDependency.new "dummy-gem-k" + dummy_gem.add_load_paths + dummy_gem.load + assert_equal 1, dummy_gem.dependencies.size + end + + def test_gem_guards_against_duplicate_unpacks + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.stubs(:frozen?).returns(true) + dummy_gem.expects(:unpack_base).never + dummy_gem.unpack + end + + def test_gem_does_not_unpack_framework_gems + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.stubs(:framework_gem?).returns(true) + dummy_gem.expects(:unpack_base).never + dummy_gem.unpack + end + + def test_gem_from_directory_name_attempts_to_load_specification + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1') + end + end + + def test_gem_from_directory_name + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1', false) + assert_equal 'dummy-gem', dummy_gem.name + assert_equal '= 1.1', dummy_gem.version_requirements.to_s + end + + def test_gem_from_directory_name_loads_specification_successfully + assert_nothing_raised do + dummy_gem = Rails::GemDependency.from_directory_name(File.join(Rails::GemDependency.unpacked_path, 'dummy-gem-g-1.0.0')) + assert_not_nil dummy_gem.specification + end + end + + def test_gem_from_invalid_directory_name + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem') + end + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy') + end + end + + def test_gem_determines_build_status + assert_equal true, Rails::GemDependency.new("dummy-gem-a").built? + assert_equal true, Rails::GemDependency.new("dummy-gem-i").built? + assert_equal false, Rails::GemDependency.new("dummy-gem-j").built? + end + + def test_gem_determines_build_status_only_on_vendor_gems + framework_gem = Rails::GemDependency.new('dummy-framework-gem') + framework_gem.stubs(:framework_gem?).returns(true) # already loaded + framework_gem.stubs(:vendor_rails?).returns(false) # but not in vendor/rails + framework_gem.stubs(:vendor_gem?).returns(false) # and not in vendor/gems + framework_gem.add_load_paths # freeze framework gem early + assert framework_gem.built? + end + + def test_gem_build_passes_options_to_dependencies + start_gem = Rails::GemDependency.new("dummy-gem-g") + dep_gem = Rails::GemDependency.new("dummy-gem-f") + start_gem.stubs(:dependencies).returns([dep_gem]) + dep_gem.expects(:build).with({ :force => true }).once + start_gem.build(:force => true) + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generator_lookup_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generator_lookup_test.rb new file mode 100644 index 00000000000..b650f304edf --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generator_lookup_test.rb @@ -0,0 +1,40 @@ +require 'plugin_test_helper' + +class GeneratorLookupTest < Test::Unit::TestCase + def setup + @fixture_dirs = %w{alternate default} + @configuration = Rails.configuration = Rails::Configuration.new + # We need to add our testing plugin directory to the plugin paths so + # the locator knows where to look for our plugins + @configuration.plugin_paths += @fixture_dirs.map{|fd| plugin_fixture_path(fd)} + @initializer = Rails::Initializer.new(@configuration) + @initializer.add_plugin_load_paths + @initializer.load_plugins + load 'rails_generator.rb' + require 'rails_generator/scripts' + end + + def test_should_load_from_all_plugin_paths + assert Rails::Generator::Base.lookup('a_generator') + assert Rails::Generator::Base.lookup('stubby_generator') + end + + def test_should_create_generator_source_for_each_directory_in_plugin_paths + sources = Rails::Generator::Base.sources + @fixture_dirs.each do |gen_dir| + expected_label = "plugins (fixtures/plugins/#{gen_dir})".to_sym + assert sources.any? {|source| source.label == expected_label } + end + end + + def test_should_preserve_order_in_usage_message + msg = Rails::Generator::Scripts::Base.new.send(:usage_message) + positions = @fixture_dirs.map do |gen_dir| + pos = msg.index("Plugins (fixtures/plugins/#{gen_dir})") + assert_not_nil pos + pos + end + assert_equal positions.sort, positions + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/generator_test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/generator_test_helper.rb new file mode 100644 index 00000000000..6f02eb381f8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/generator_test_helper.rb @@ -0,0 +1,310 @@ +require 'test/unit' +require 'fileutils' + +# Mock out what we need from AR::Base +module ActiveRecord + class Base + class << self + attr_accessor :pluralize_table_names, :timestamped_migrations + end + self.pluralize_table_names = true + self.timestamped_migrations = true + end + + module ConnectionAdapters + class Column + attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale + + def initialize(name, default, sql_type = nil) + @name = name + @default = default + @type = @sql_type = sql_type + end + + def human_name + @name.humanize + end + end + end +end + +# Mock up necessities from ActionView +module ActionView + module Helpers + module ActionRecordHelper; end + class InstanceTag; end + end +end + +# Set RAILS_ROOT appropriately fixture generation +tmp_dir = "#{File.dirname(__FILE__)}/../fixtures/tmp" + +if defined? RAILS_ROOT + RAILS_ROOT.replace tmp_dir +else + RAILS_ROOT = tmp_dir +end +FileUtils.mkdir_p RAILS_ROOT + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" +require 'initializer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +require 'rails_generator' + +class GeneratorTestCase < Test::Unit::TestCase + include FileUtils + + def setup + ActiveRecord::Base.pluralize_table_names = true + + mkdir_p "#{RAILS_ROOT}/app/views/layouts" + mkdir_p "#{RAILS_ROOT}/config" + mkdir_p "#{RAILS_ROOT}/db" + mkdir_p "#{RAILS_ROOT}/test/fixtures" + mkdir_p "#{RAILS_ROOT}/public/stylesheets" + + File.open("#{RAILS_ROOT}/config/routes.rb", 'w') do |f| + f << "ActionController::Routing::Routes.draw do |map|\n\nend" + end + end + + def teardown + rm_rf "#{RAILS_ROOT}/app" + rm_rf "#{RAILS_ROOT}/test" + rm_rf "#{RAILS_ROOT}/config" + rm_rf "#{RAILS_ROOT}/db" + rm_rf "#{RAILS_ROOT}/public" + end + + def test_truth + # don't complain, test/unit + end + + # Instantiates the Generator. + def build_generator(name, params) + Rails::Generator::Base.instance(name, params) + end + + # Runs the +create+ command (like the command line does). + def run_generator(name, params) + silence_generator do + build_generator(name, params).command(:create).invoke! + end + end + + # Silences the logger temporarily and returns the output as a String. + def silence_generator + logger_original = Rails::Generator::Base.logger + myout = StringIO.new + Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(myout) + yield if block_given? + Rails::Generator::Base.logger = logger_original + myout.string + end + + # Asserts that the given controller was generated. + # It takes a name or symbol without the _controller part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_controller_for(name, parent = "ApplicationController") + assert_generated_class "app/controllers/#{name.to_s.underscore}_controller", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given model was generated. + # It takes a name or symbol and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_model_for(name, parent = "ActiveRecord::Base") + assert_generated_class "app/models/#{name.to_s.underscore}", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given helper was generated. + # It takes a name or symbol without the _helper part. + # The contents of the module source file is passed to a block. + def assert_generated_helper_for(name) + assert_generated_module "app/helpers/#{name.to_s.underscore}_helper" do |body| + yield body if block_given? + end + end + + # Asserts that the given functional test was generated. + # It takes a name or symbol without the _controller_test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_functional_test_for(name, parent = "ActionController::TestCase") + assert_generated_class "test/functional/#{name.to_s.underscore}_controller_test",parent do |body| + yield body if block_given? + end + end + + # Asserts that the given helper test test was generated. + # It takes a name or symbol without the _helper_test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_helper_test_for(name, parent = "ActionView::TestCase") + path = "test/unit/helpers/#{name.to_s.underscore}_helper_test" + # Have to pass the path without the "test/" part so that class_name_from_path will return a correct result + class_name = class_name_from_path(path.gsub(/^test\//, '')) + + assert_generated_class path,parent,class_name do |body| + yield body if block_given? + end + end + + # Asserts that the given unit test was generated. + # It takes a name or symbol without the _test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_unit_test_for(name, parent = "ActiveSupport::TestCase") + assert_generated_class "test/unit/#{name.to_s.underscore}_test", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given file was generated. + # The contents of the file is passed to a block. + def assert_generated_file(path) + assert_file_exists(path) + File.open("#{RAILS_ROOT}/#{path}") do |f| + yield f.read if block_given? + end + end + + # asserts that the given file exists + def assert_file_exists(path) + assert File.exist?("#{RAILS_ROOT}/#{path}"), + "The file '#{RAILS_ROOT}/#{path}' should exist" + end + + # Asserts that the given class source file was generated. + # It takes a path without the .rb part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_class(path, parent = nil, class_name = class_name_from_path(path)) + assert_generated_file("#{path}.rb") do |body| + assert_match /class #{class_name}#{parent.nil? ? '':" < #{parent}"}/, body, "the file '#{path}.rb' should be a class" + yield body if block_given? + end + end + + def class_name_from_path(path) + # FIXME: Sucky way to detect namespaced classes + if path.split('/').size > 3 + path =~ /\/?(\d+_)?(\w+)\/(\w+)$/ + "#{$2.camelize}::#{$3.camelize}" + else + path =~ /\/?(\d+_)?(\w+)$/ + $2.camelize + end + end + + # Asserts that the given module source file was generated. + # It takes a path without the .rb part. + # The contents of the class source file is passed to a block. + def assert_generated_module(path) + # FIXME: Sucky way to detect namespaced modules + if path.split('/').size > 3 + path =~ /\/?(\w+)\/(\w+)$/ + module_name = "#{$1.camelize}::#{$2.camelize}" + else + path =~ /\/?(\w+)$/ + module_name = $1.camelize + end + + assert_generated_file("#{path}.rb") do |body| + assert_match /module #{module_name}/, body, "the file '#{path}.rb' should be a module" + yield body if block_given? + end + end + + # Asserts that the given CSS stylesheet file was generated. + # It takes a path without the .css part. + # The contents of the stylesheet source file is passed to a block. + def assert_generated_stylesheet(path) + assert_generated_file("public/stylesheets/#{path}.css") do |body| + yield body if block_given? + end + end + + # Asserts that the given YAML file was generated. + # It takes a path without the .yml part. + # The parsed YAML tree is passed to a block. + def assert_generated_yaml(path) + assert_generated_file("#{path}.yml") do |body| + yaml = YAML.load(body) + assert yaml, 'YAML data missing' + yield yaml if block_given? + end + end + + # Asserts that the given fixtures YAML file was generated. + # It takes a fixture name without the .yml part. + # The parsed YAML tree is passed to a block. + def assert_generated_fixtures_for(name) + assert_generated_yaml "test/fixtures/#{name.to_s.underscore}" do |yaml| + yield yaml if block_given? + end + end + + # Asserts that the given views were generated. + # It takes a controller name and a list of views (including extensions). + # The body of each view is passed to a block. + def assert_generated_views_for(name, *actions) + actions.each do |action| + assert_generated_file("app/views/#{name.to_s.underscore}/#{action}") do |body| + yield body if block_given? + end + end + end + + def assert_generated_migration(name, parent = "ActiveRecord::Migration") + file = Dir.glob("#{RAILS_ROOT}/db/migrate/*_#{name.to_s.underscore}.rb").first + assert !file.nil?, "should have generated the migration file but didn't" + + file = file.match(/db\/migrate\/[0-9]+_\w+/).to_s + assert_generated_class file, parent do |body| + assert_match /timestamps/, body, "should have timestamps defined" + yield body if block_given? + end + end + + # Asserts that the given migration file was not generated. + # It takes the name of the migration as a parameter. + def assert_skipped_migration(name) + migration_file = "#{RAILS_ROOT}/db/migrate/001_#{name.to_s.underscore}.rb" + assert !File.exist?(migration_file), "should not create migration #{migration_file}" + end + + # Asserts that the given resource was added to the routes. + def assert_added_route_for(name) + assert_generated_file("config/routes.rb") do |body| + assert_match /map.resources :#{name.to_s.underscore}/, body, + "should add route for :#{name.to_s.underscore}" + end + end + + # Asserts that the given methods are defined in the body. + # This does assume standard rails code conventions with regards to the source code. + # The body of each individual method is passed to a block. + def assert_has_method(body, *methods) + methods.each do |name| + assert body =~ /^ def #{name}(\(.+\))?\n((\n| .*\n)*) end/, "should have method #{name}" + yield(name, $2) if block_given? + end + end + + # Asserts that the given column is defined in the migration. + def assert_generated_column(body, name, type) + assert_match /t\.#{type.to_s} :#{name.to_s}/, body, "should have column #{name.to_s} defined" + end + + # Asserts that the given table is defined in the migration. + def assert_generated_table(body, name) + assert_match /create_table :#{name.to_s} do/, body, "should have table #{name.to_s} defined" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_controller_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_controller_generator_test.rb new file mode 100644 index 00000000000..43fbe972e2e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_controller_generator_test.rb @@ -0,0 +1,44 @@ +require 'generators/generator_test_helper' + +module Admin +end + +class RailsControllerGeneratorTest < GeneratorTestCase + + def test_controller_generates_controller + run_generator('controller', %w(products)) + + assert_generated_controller_for :products + assert_generated_functional_test_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + end + + def test_controller_generates_namespaced_controller + run_generator('controller', %w(admin::products)) + + assert_generated_controller_for "admin::products" + assert_generated_functional_test_for "admin::products" + assert_generated_helper_for "admin::products" + assert_generated_helper_test_for "admin::products" + end + + def test_controller_generates_namespaced_and_not_namespaced_controllers + run_generator('controller', %w(products)) + + # We have to require the generated helper to show the problem because + # the test helpers just check for generated files and contents but + # do not actually load them. But they have to be loaded (as in a real environment) + # to make the second generator run fail + require "#{RAILS_ROOT}/app/helpers/products_helper" + + assert_nothing_raised do + begin + run_generator('controller', %w(admin::products)) + ensure + # cleanup + Object.send(:remove_const, :ProductsHelper) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_helper_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_helper_generator_test.rb new file mode 100644 index 00000000000..8d05f555e65 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_helper_generator_test.rb @@ -0,0 +1,36 @@ +require File.dirname(__FILE__) + '/generator_test_helper' + +class RailsHelperGeneratorTest < GeneratorTestCase + def test_helper_generates_helper + run_generator('helper', %w(products)) + + assert_generated_helper_for :products + assert_generated_helper_test_for :products + end + + def test_helper_generates_namespaced_helper + run_generator('helper', %w(admin::products)) + + assert_generated_helper_for "admin::products" + assert_generated_helper_test_for "admin::products" + end + + def test_helper_generates_namespaced_and_not_namespaced_helpers + run_generator('helper', %w(products)) + + # We have to require the generated helper to show the problem because + # the test helpers just check for generated files and contents but + # do not actually load them. But they have to be loaded (as in a real environment) + # to make the second generator run fail + require "#{RAILS_ROOT}/app/helpers/products_helper" + + assert_nothing_raised do + begin + run_generator('helper', %w(admin::products)) + ensure + # cleanup + Object.send(:remove_const, :ProductsHelper) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_mailer_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_mailer_generator_test.rb new file mode 100644 index 00000000000..de61e6736d8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_mailer_generator_test.rb @@ -0,0 +1,29 @@ +require 'generators/generator_test_helper' + +class RailsMailerGeneratorTest < GeneratorTestCase + + def test_generates_mailer + run_generator('mailer', %w(Notifier reset_password)) + + assert_generated_model_for :notifier, 'ActionMailer::Base' do |model| + assert_has_method model, :reset_password do |name, body| + assert_equal [ + "subject 'Notifier#reset_password'", + "recipients ''", + "from ''", + "sent_on sent_at", + "", + "body :greeting => 'Hi,'" + ], + body.split("\n").map{|line| line.sub(' '*4, '') } + end + + assert_no_match /(self.default_url_options =|default_url_options\[.*\] =)/, model, + 'individual mailer models should not set default_url_options because the options are shared by all mailers' + end + + assert_generated_views_for :notifier, 'reset_password.erb' + assert_generated_unit_test_for :notifier, 'ActionMailer::TestCase' + assert_generated_file "test/fixtures/notifier/reset_password" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_model_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_model_generator_test.rb new file mode 100644 index 00000000000..208b0c49224 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_model_generator_test.rb @@ -0,0 +1,96 @@ +require 'generators/generator_test_helper' + +class RailsModelGeneratorTest < GeneratorTestCase + + def test_model_generates_resources + run_generator('model', %w(Product name:string)) + + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_migration :create_products + end + + def test_model_skip_migration_skips_migration + run_generator('model', %w(Product name:string --skip-migration)) + + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_skipped_migration :create_products + end + + def test_model_with_attributes_generates_resources_with_attributes + run_generator('model', %w(Product name:string supplier_id:integer created_at:timestamp)) + + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_migration :create_products do |t| + assert_generated_column t, :name, :string + assert_generated_column t, :supplier_id, :integer + assert_generated_column t, :created_at, :timestamp + end + end + + def test_model_with_reference_attributes_generates_belongs_to_associations + run_generator('model', %w(Product name:string supplier:references)) + + assert_generated_model_for :product do |body| + assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" + end + end + + def test_model_with_belongs_to_attributes_generates_belongs_to_associations + run_generator('model', %w(Product name:string supplier:belongs_to)) + + assert_generated_model_for :product do |body| + assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" + end + end + + def test_migration_with_namespace + run_generator('model', %w(Gallery::Image)) + assert_generated_migration :gallery_images + assert_skipped_migration :create_images + end + + def test_migration_with_nested_namespace + run_generator('model', %w(Admin::Gallery::Image)) + assert_skipped_migration :create_images + assert_skipped_migration :create_gallery_images + + assert_generated_migration :admin_gallery_images do |t| + assert_generated_table t, :admin_gallery_images + end + end + + def test_migration_with_nested_namespace_without_pluralization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Admin::Gallery::Image)) + assert_skipped_migration :create_images + assert_skipped_migration :create_gallery_images + assert_skipped_migration :create_admin_gallery_images + assert_generated_migration :admin_gallery_image do |t| + assert_generated_table t, :admin_gallery_image + end + ensure + ActiveRecord::Base.pluralize_table_names = true + end + + def test_migration_with_namespaces_in_model_name_without_plurization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Gallery::Image)) + assert_generated_migration :create_gallery_image + assert_skipped_migration :create_gallery_images + ensure + ActiveRecord::Base.pluralize_table_names = true + end + + def test_migration_without_pluralization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Account)) + assert_generated_migration :create_account + assert_skipped_migration :create_accounts + ensure + ActiveRecord::Base.pluralize_table_names = true + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_resource_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_resource_generator_test.rb new file mode 100644 index 00000000000..1f5bd0ef1e6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_resource_generator_test.rb @@ -0,0 +1,29 @@ +require 'generators/generator_test_helper' + +class RailsResourceGeneratorTest < GeneratorTestCase + def test_resource_generates_resources + run_generator('resource', %w(Product name:string)) + + assert_generated_controller_for :products + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_functional_test_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_migration :create_products + assert_added_route_for :products + end + + def test_resource_skip_migration_skips_migration + run_generator('resource', %w(Product name:string --skip-migration)) + + assert_generated_controller_for :products + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_functional_test_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_skipped_migration :create_products + assert_added_route_for :products + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_scaffold_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_scaffold_generator_test.rb new file mode 100644 index 00000000000..2c60eb07d8d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_scaffold_generator_test.rb @@ -0,0 +1,151 @@ +require 'generators/generator_test_helper' +require 'abstract_unit' + +class RailsScaffoldGeneratorTest < GeneratorTestCase + def test_scaffolded_names + g = Rails::Generator::Base.instance('scaffold', %w(ProductLine)) + assert_equal "ProductLines", g.controller_name + assert_equal "ProductLines", g.controller_class_name + assert_equal "ProductLine", g.controller_singular_name + assert_equal "product_lines", g.controller_plural_name + assert_equal "product_lines", g.controller_file_name + assert_equal "product_lines", g.controller_table_name + end + + def test_scaffold_generates_resources + + run_generator('scaffold', %w(Product name:string)) + + assert_generated_controller_for :products do |f| + + assert_has_method f, :index do |name, m| + assert_match /@products = Product\.all/, m, "#{name} should query products table" + end + + assert_has_method f, :show, :edit, :update, :destroy do |name, m| + assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table" + end + + assert_has_method f, :new do |name, m| + assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product" + end + + assert_has_method f, :create do |name, m| + assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product" + assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml" + end + + end + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb" + + assert_generated_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_skip_migration_skips_migration + run_generator('scaffold', %w(Product name:string --skip-migration)) + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_generates_resources_with_attributes + run_generator('scaffold', %w(Product name:string supplier_id:integer created_at:timestamp)) + + assert_generated_controller_for :products do |f| + + assert_has_method f, :index do |name, m| + assert_match /@products = Product\.all/, m, "#{name} should query products table" + end + + assert_has_method f, :show, :edit, :update, :destroy do |name, m| + assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table" + end + + assert_has_method f, :new do |name, m| + assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product" + end + + assert_has_method f, :create do |name, m| + assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product" + assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml" + end + + end + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb" + + assert_generated_migration :create_products do |t| + assert_generated_column t, :name, :string + assert_generated_column t, :supplier_id, :integer + assert_generated_column t, :created_at, :timestamp + end + + assert_added_route_for :products + end + + def test_scaffolded_plural_names + Rails::Generator::Base.logger.expects(:warning) + g = Rails::Generator::Base.instance('scaffold', %w(ProductLines)) + assert_equal "ProductLines", g.controller_name + assert_equal "ProductLines", g.controller_class_name + assert_equal "ProductLine", g.controller_singular_name + assert_equal "product_lines", g.controller_plural_name + assert_equal "product_lines", g.controller_file_name + assert_equal "product_lines", g.controller_table_name + assert_equal "ProductLine", g.class_name + end + + def test_scaffold_plural_model_name_without_force_plural_generates_singular_model + run_generator('scaffold', %w(Products name:string)) + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_plural_model_name_with_force_plural_forces_plural_model + run_generator('scaffold', %w(Products name:string --force-plural)) + + assert_generated_model_for :products + assert_generated_functional_test_for :products + assert_generated_unit_test_for :products + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_template_runner_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_template_runner_test.rb new file mode 100644 index 00000000000..2da6bd59b57 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/generators/rails_template_runner_test.rb @@ -0,0 +1,216 @@ +require 'abstract_unit' +require 'generators/generator_test_helper' + +class RailsTemplateRunnerTest < GeneratorTestCase + def setup + Rails::Generator::Base.use_application_sources! + run_generator('app', [RAILS_ROOT]) + # generate empty template + @template_path = File.join(RAILS_ROOT, 'template.rb') + File.open(File.join(@template_path), 'w') {|f| f << '' } + + @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git' + @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + end + + def teardown + super + rm_rf "#{RAILS_ROOT}/README" + rm_rf "#{RAILS_ROOT}/Rakefile" + rm_rf "#{RAILS_ROOT}/doc" + rm_rf "#{RAILS_ROOT}/lib" + rm_rf "#{RAILS_ROOT}/log" + rm_rf "#{RAILS_ROOT}/script" + rm_rf "#{RAILS_ROOT}/vendor" + rm_rf "#{RAILS_ROOT}/tmp" + rm_rf "#{RAILS_ROOT}/Capfile" + rm_rf @template_path + end + + def test_initialize_should_load_template + Rails::TemplateRunner.any_instance.expects(:load_template).with(@template_path) + silence_generator do + Rails::TemplateRunner.new(@template_path, RAILS_ROOT) + end + end + + def test_initialize_should_raise_error_on_missing_template_file + assert_raise(RuntimeError) do + silence_generator do + Rails::TemplateRunner.new('non/existent/path/to/template.rb', RAILS_ROOT) + end + end + end + + def test_file_should_write_data_to_file_path + run_template_method(:file, 'lib/test_file.rb', 'heres test data') + assert_generated_file_with_data 'lib/test_file.rb', 'heres test data' + end + + def test_file_should_write_block_contents_to_file_path + run_template_method(:file, 'lib/test_file.rb') { 'heres block data' } + assert_generated_file_with_data 'lib/test_file.rb', 'heres block data' + end + + def test_plugin_with_git_option_should_run_plugin_install + expects_run_ruby_script_with_command("script/plugin install #{@git_plugin_uri}") + run_template_method(:plugin, 'restful-authentication', :git => @git_plugin_uri) + end + + def test_plugin_with_svn_option_should_run_plugin_install + expects_run_ruby_script_with_command("script/plugin install #{@svn_plugin_uri}") + run_template_method(:plugin, 'restful-authentication', :svn => @svn_plugin_uri) + end + + def test_plugin_with_git_option_and_submodule_should_use_git_scm + Rails::Git.expects(:run).with("submodule add #{@git_plugin_uri} vendor/plugins/rest_auth") + run_template_method(:plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true) + end + + def test_plugin_with_no_options_should_skip_method + Rails::TemplateRunner.any_instance.expects(:run).never + run_template_method(:plugin, 'rest_auth', {}) + end + + def test_gem_should_put_gem_dependency_in_enviroment + run_template_method(:gem, 'will-paginate') + assert_rails_initializer_includes("config.gem 'will-paginate'") + end + + def test_gem_with_options_should_include_options_in_gem_dependency_in_environment + run_template_method(:gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com') + assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'") + end + + def test_gem_with_env_string_should_put_gem_dependency_in_specified_environment + run_template_method(:gem, 'rspec', :env => 'test') + assert_generated_file_with_data('config/environments/test.rb', "config.gem 'rspec'", 'test') + end + + def test_gem_with_env_array_should_put_gem_dependency_in_specified_environments + run_template_method(:gem, 'quietbacktrace', :env => %w[ development test ]) + assert_generated_file_with_data('config/environments/development.rb', "config.gem 'quietbacktrace'") + assert_generated_file_with_data('config/environments/test.rb', "config.gem 'quietbacktrace'") + end + + def test_gem_with_lib_option_set_to_false_should_put_gem_dependency_in_enviroment_correctly + run_template_method(:gem, 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com') + assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com'") + end + + def test_environment_should_include_data_in_environment_initializer_block + load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]' + run_template_method(:environment, load_paths) + assert_rails_initializer_includes(load_paths) + end + + def test_environment_with_block_should_include_block_contents_in_environment_initializer_block + run_template_method(:environment) do + '# This wont be added' + '# This will be added' + end + assert_rails_initializer_includes('# This will be added') + end + + def test_git_with_symbol_should_run_command_using_git_scm + Rails::Git.expects(:run).once.with('init') + run_template_method(:git, :init) + end + + def test_git_with_hash_should_run_each_command_using_git_scm + Rails::Git.expects(:run).times(2) + run_template_method(:git, {:init => '', :add => '.'}) + end + + def test_vendor_should_write_data_to_file_in_vendor + run_template_method(:vendor, 'vendor_file.rb', '# vendor data') + assert_generated_file_with_data('vendor/vendor_file.rb', '# vendor data') + end + + def test_lib_should_write_data_to_file_in_lib + run_template_method(:lib, 'my_library.rb', 'class MyLibrary') + assert_generated_file_with_data('lib/my_library.rb', 'class MyLibrary') + end + + def test_rakefile_should_write_date_to_file_in_lib_tasks + run_template_method(:rakefile, 'myapp.rake', 'task :run => [:environment]') + assert_generated_file_with_data('lib/tasks/myapp.rake', 'task :run => [:environment]') + end + + def test_initializer_should_write_date_to_file_in_config_initializers + run_template_method(:initializer, 'constants.rb', 'MY_CONSTANT = 42') + assert_generated_file_with_data('config/initializers/constants.rb', 'MY_CONSTANT = 42') + end + + def test_generate_should_run_script_generate_with_argument_and_options + expects_run_ruby_script_with_command('script/generate model MyModel') + run_template_method(:generate, 'model', 'MyModel') + end + + def test_rake_should_run_rake_command_with_development_env + expects_run_with_command('rake log:clear RAILS_ENV=development') + run_template_method(:rake, 'log:clear') + end + + def test_rake_with_env_option_should_run_rake_command_in_env + expects_run_with_command('rake log:clear RAILS_ENV=production') + run_template_method(:rake, 'log:clear', :env => 'production') + end + + def test_rake_with_sudo_option_should_run_rake_command_with_sudo + expects_run_with_command('sudo rake log:clear RAILS_ENV=development') + run_template_method(:rake, 'log:clear', :sudo => true) + end + + def test_capify_should_run_the_capify_command + expects_run_with_command('capify .') + run_template_method(:capify!) + end + + def test_freeze_should_freeze_rails_edge + expects_run_with_command('rake rails:freeze:edge') + run_template_method(:freeze!) + end + + def test_route_should_add_data_to_the_routes_block_in_config_routes + route_command = "map.route '/login', :controller => 'sessions', :action => 'new'" + run_template_method(:route, route_command) + assert_generated_file_with_data 'config/routes.rb', route_command + end + + def test_run_ruby_script_should_add_ruby_to_command_in_win32_environment + ruby_command = RUBY_PLATFORM =~ /win32/ ? 'ruby ' : '' + expects_run_with_command("#{ruby_command}script/generate model MyModel") + run_template_method(:generate, 'model', 'MyModel') + end + + protected + def run_template_method(method_name, *args, &block) + silence_generator do + @template_runner = Rails::TemplateRunner.new(@template_path, RAILS_ROOT) + @template_runner.send(method_name, *args, &block) + end + end + + def expects_run_with_command(command) + Rails::TemplateRunner.any_instance.stubs(:run).once.with(command, false) + end + + def expects_run_ruby_script_with_command(command) + Rails::TemplateRunner.any_instance.stubs(:run_ruby_script).once.with(command,false) + end + + def assert_rails_initializer_includes(data, message = nil) + message ||= "Rails::Initializer should include #{data}" + assert_generated_file 'config/environment.rb' do |body| + assert_match(/#{Regexp.escape("Rails::Initializer.run do |config|")}.+#{Regexp.escape(data)}.+end/m, body, message) + end + end + + def assert_generated_file_with_data(file, data, message = nil) + message ||= "#{file} should include '#{data}'" + assert_generated_file(file) do |file| + assert_match(/#{Regexp.escape(data)}/,file, message) + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/initializer_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/initializer_test.rb new file mode 100644 index 00000000000..68c99ee06b6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/initializer_test.rb @@ -0,0 +1,416 @@ +require 'abstract_unit' +require 'initializer' + +require 'action_view' +require 'action_mailer' +require 'active_record' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +class ConfigurationMock < Rails::Configuration + attr_reader :environment_path + + def initialize(envpath) + super() + @environment_path = envpath + end +end + +class Initializer_load_environment_Test < Test::Unit::TestCase + def test_load_environment_with_constant + config = ConfigurationMock.new("#{File.dirname(__FILE__)}/fixtures/environment_with_constant.rb") + assert_nil $initialize_test_set_from_env + Rails::Initializer.run(:load_environment, config) + assert_equal "success", $initialize_test_set_from_env + ensure + $initialize_test_set_from_env = nil + end +end + +class Initializer_eager_loading_Test < Test::Unit::TestCase + def setup + @config = ConfigurationMock.new("") + @config.cache_classes = true + @config.load_paths = [File.expand_path(File.dirname(__FILE__) + "/fixtures/eager")] + @config.eager_load_paths = [File.expand_path(File.dirname(__FILE__) + "/fixtures/eager")] + @initializer = Rails::Initializer.new(@config) + @initializer.set_load_path + @initializer.set_autoload_paths + end + + def test_eager_loading_loads_parent_classes_before_children + assert_nothing_raised do + @initializer.load_application_classes + end + end +end + +class Initializer_after_initialize_with_blocks_environment_Test < Test::Unit::TestCase + def setup + config = ConfigurationMock.new("") + config.after_initialize do + $test_after_initialize_block1 = "success" + end + config.after_initialize do + $test_after_initialize_block2 = "congratulations" + end + assert_nil $test_after_initialize_block1 + assert_nil $test_after_initialize_block2 + + Rails::Initializer.any_instance.expects(:gems_dependencies_loaded).returns(true) + Rails::Initializer.run(:after_initialize, config) + end + + def teardown + $test_after_initialize_block1 = nil + $test_after_initialize_block2 = nil + end + + def test_should_have_called_the_first_after_initialize_block + assert_equal "success", $test_after_initialize_block1 + end + + def test_should_have_called_the_second_after_initialize_block + assert_equal "congratulations", $test_after_initialize_block2 + end +end + +class Initializer_after_initialize_with_no_block_environment_Test < Test::Unit::TestCase + def setup + config = ConfigurationMock.new("") + config.after_initialize do + $test_after_initialize_block1 = "success" + end + config.after_initialize # don't pass a block, this is what we're testing! + config.after_initialize do + $test_after_initialize_block2 = "congratulations" + end + assert_nil $test_after_initialize_block1 + + Rails::Initializer.any_instance.expects(:gems_dependencies_loaded).returns(true) + Rails::Initializer.run(:after_initialize, config) + end + + def teardown + $test_after_initialize_block1 = nil + $test_after_initialize_block2 = nil + end + + def test_should_have_called_the_first_after_initialize_block + assert_equal "success", $test_after_initialize_block1, "should still get set" + end + + def test_should_have_called_the_second_after_initialize_block + assert_equal "congratulations", $test_after_initialize_block2 + end +end + +class ConfigurationFrameworkPathsTests < Test::Unit::TestCase + def setup + @config = Rails::Configuration.new + @config.frameworks.clear + + File.stubs(:directory?).returns(true) + @config.stubs(:framework_root_path).returns('') + end + + def test_minimal + expected = %w( + /railties + /railties/lib + /activesupport/lib + ) + assert_equal expected, @config.framework_paths + end + + def test_actioncontroller_or_actionview_add_actionpack + @config.frameworks << :action_controller + assert_framework_path '/actionpack/lib' + + @config.frameworks = [:action_view] + assert_framework_path '/actionpack/lib' + end + + def test_paths_for_ar_ares_and_mailer + [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework| + @config.frameworks = [framework] + assert_framework_path "/#{framework.to_s.gsub('_', '')}/lib" + end + end + + def test_unknown_framework_raises_error + @config.frameworks << :action_foo + initializer = Rails::Initializer.new @config + initializer.expects(:require).raises(LoadError) + + assert_raise RuntimeError do + initializer.send :require_frameworks + end + end + + def test_action_mailer_load_paths_set_only_if_action_mailer_in_use + @config.frameworks = [:action_controller] + initializer = Rails::Initializer.new @config + initializer.send :require_frameworks + + assert_nothing_raised NameError do + initializer.send :load_view_paths + end + end + + def test_action_controller_load_paths_set_only_if_action_controller_in_use + @config.frameworks = [] + initializer = Rails::Initializer.new @config + initializer.send :require_frameworks + + assert_nothing_raised NameError do + initializer.send :load_view_paths + end + end + + protected + def assert_framework_path(path) + assert @config.framework_paths.include?(path), + "<#{path.inspect}> not found among <#{@config.framework_paths.inspect}>" + end +end + +require File.dirname(__FILE__) + '/plugin_test_helper' + +class InitializerPluginLoadingTests < Test::Unit::TestCase + def setup + @configuration = Rails::Configuration.new + @configuration.frameworks -= [:action_mailer] + @configuration.plugin_paths << plugin_fixture_root_path + @initializer = Rails::Initializer.new(@configuration) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + end + + def test_no_plugins_are_loaded_if_the_configuration_has_an_empty_plugin_list + only_load_the_following_plugins! [] + @initializer.load_plugins + assert_equal [], @initializer.loaded_plugins + end + + def test_only_the_specified_plugins_are_located_in_the_order_listed + plugin_names = [:plugin_with_no_lib_dir, :acts_as_chunky_bacon] + only_load_the_following_plugins! plugin_names + load_plugins! + assert_plugins plugin_names, @initializer.loaded_plugins + end + + def test_all_plugins_are_loaded_when_registered_plugin_list_is_untouched + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + load_plugins! + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @initializer.loaded_plugins, failure_tip + end + + def test_all_plugins_loaded_when_all_is_used + plugin_names = [:stubby, :acts_as_chunky_bacon, :all] + only_load_the_following_plugins! plugin_names + load_plugins! + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @initializer.loaded_plugins, failure_tip + end + + def test_all_plugins_loaded_after_all + plugin_names = [:stubby, :all, :acts_as_chunky_bacon] + only_load_the_following_plugins! plugin_names + load_plugins! + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @initializer.loaded_plugins, failure_tip + end + + def test_plugin_names_may_be_strings + plugin_names = ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir] + only_load_the_following_plugins! plugin_names + load_plugins! + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + assert_plugins plugin_names, @initializer.loaded_plugins, failure_tip + end + + def test_registering_a_plugin_name_that_does_not_exist_raises_a_load_error + only_load_the_following_plugins! [:stubby, :acts_as_a_non_existant_plugin] + assert_raise(LoadError) do + load_plugins! + end + end + + def test_load_error_messages_mention_missing_plugins_and_no_others + valid_plugin_names = [:stubby, :acts_as_chunky_bacon] + invalid_plugin_names = [:non_existant_plugin1, :non_existant_plugin2] + only_load_the_following_plugins!( valid_plugin_names + invalid_plugin_names ) + begin + load_plugins! + flunk "Expected a LoadError but did not get one" + rescue LoadError => e + failure_tip = "It's likely someone renamed or deleted plugin fixtures without updating this test" + assert_plugins valid_plugin_names, @initializer.loaded_plugins, failure_tip + invalid_plugin_names.each do |plugin| + assert_match(/#{plugin.to_s}/, e.message, "LoadError message should mention plugin '#{plugin}'") + end + valid_plugin_names.each do |plugin| + assert_no_match(/#{plugin.to_s}/, e.message, "LoadError message should not mention '#{plugin}'") + end + + end + end + + def test_should_ensure_all_loaded_plugins_load_paths_are_added_to_the_load_path + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + + @initializer.add_plugin_load_paths + + assert $LOAD_PATH.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) + assert $LOAD_PATH.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) + end + + private + + def load_plugins! + @initializer.add_plugin_load_paths + @initializer.load_plugins + end +end + +class InitializerSetupI18nTests < Test::Unit::TestCase + def test_no_config_locales_dir_present_should_return_empty_load_path + File.stubs(:exist?).returns(false) + assert_equal [], Rails::Configuration.new.i18n.load_path + end + + def test_config_locales_dir_present_should_be_added_to_load_path + File.stubs(:exist?).returns(true) + Dir.stubs(:[]).returns([ "my/test/locale.yml" ]) + assert_equal [ "my/test/locale.yml" ], Rails::Configuration.new.i18n.load_path + end + + def test_config_defaults_should_be_added_with_config_settings + File.stubs(:exist?).returns(true) + Dir.stubs(:[]).returns([ "my/test/locale.yml" ]) + + config = Rails::Configuration.new + config.i18n.load_path << "my/other/locale.yml" + + assert_equal [ "my/test/locale.yml", "my/other/locale.yml" ], config.i18n.load_path + end + + def test_config_defaults_and_settings_should_be_added_to_i18n_defaults + File.stubs(:exist?).returns(true) + Dir.stubs(:[]).returns([ "my/test/locale.yml" ]) + + config = Rails::Configuration.new + config.i18n.load_path << "my/other/locale.yml" + + Rails::Initializer.run(:initialize_i18n, config) + assert_equal [ + File.expand_path(File.dirname(__FILE__) + "/../../activesupport/lib/active_support/locale/en.yml"), + File.expand_path(File.dirname(__FILE__) + "/../../actionpack/lib/action_view/locale/en.yml"), + File.expand_path(File.dirname(__FILE__) + "/../../activerecord/lib/active_record/locale/en.yml"), + # FIXME: should clean I18n.load_path between each test + File.expand_path(File.dirname(__FILE__) + "/../../railties/test/fixtures/plugins/engines/engine/config/locales/en.yml"), + "my/test/locale.yml", + "my/other/locale.yml" ], I18n.load_path.collect { |path| path =~ /^\./ ? File.expand_path(path) : path } + end + + def test_setting_another_default_locale + config = Rails::Configuration.new + config.i18n.default_locale = :de + Rails::Initializer.run(:initialize_i18n, config) + assert_equal :de, I18n.default_locale + end +end + +class InitializerDatabaseMiddlewareTest < Test::Unit::TestCase + def setup + @config = Rails::Configuration.new + @config.frameworks = [:active_record, :action_controller, :action_view] + end + + def test_initialize_database_middleware_doesnt_perform_anything_when_active_record_not_in_frameworks + @config.frameworks.clear + @config.expects(:middleware).never + Rails::Initializer.run(:initialize_database_middleware, @config) + end + + def test_database_middleware_initializes_when_session_store_is_active_record + store = ActionController::Base.session_store + ActionController::Base.session_store = ActiveRecord::SessionStore + + @config.middleware.expects(:insert_before).with(:"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:insert_before).with(:"ActiveRecord::SessionStore", ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + end + + def test_database_middleware_doesnt_initialize_when_session_store_is_not_active_record + store = ActionController::Base.session_store + ActionController::Base.session_store = ActionController::Session::CookieStore + + # Define the class, so we don't have to actually make it load + eval("class ActiveRecord::ConnectionAdapters::ConnectionManagement; end") + + @config.middleware.expects(:use).with(ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:use).with(ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + end + + def test_ensure_database_middleware_doesnt_use_action_controller_on_initializing + @config.frameworks -= [:action_controller] + store = ActionController::Base.session_store + ActionController::Base.session_store = ActiveRecord::SessionStore + + @config.middleware.expects(:use).with(ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:use).with(ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + @config.frameworks += [:action_controller] + end +end + +class InitializerViewPathsTest < Test::Unit::TestCase + def setup + @config = Rails::Configuration.new + @config.frameworks = [:action_view, :action_controller, :action_mailer] + + ActionController::Base.stubs(:view_paths).returns(stub) + ActionMailer::Base.stubs(:view_paths).returns(stub) + end + + def test_load_view_paths_doesnt_perform_anything_when_action_view_not_in_frameworks + @config.frameworks -= [:action_view] + ActionController::Base.view_paths.expects(:load!).never + ActionMailer::Base.view_paths.expects(:load!).never + Rails::Initializer.run(:load_view_paths, @config) + end + + def test_load_view_paths_loads_view_paths + ActionController::Base.view_paths.expects(:load!) + ActionMailer::Base.view_paths.expects(:load!) + Rails::Initializer.run(:load_view_paths, @config) + end +end + +class RailsRootTest < Test::Unit::TestCase + def test_rails_dot_root_equals_rails_root + assert_equal RAILS_ROOT, Rails.root.to_s + end + + def test_rails_dot_root_should_be_a_pathname + assert_equal File.join(RAILS_ROOT, 'app', 'controllers'), Rails.root.join('app', 'controllers').to_s + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/metal_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/metal_test.rb new file mode 100644 index 00000000000..d3d231132b8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/metal_test.rb @@ -0,0 +1,72 @@ +require 'abstract_unit' +require 'initializer' + +class MetalTest < Test::Unit::TestCase + def test_metals_should_return_list_of_found_metal_apps + use_appdir("singlemetal") do + assert_equal(["FooMetal"], found_metals_as_string_array) + end + end + + def test_metals_should_respect_class_name_conventions + use_appdir("pluralmetal") do + assert_equal(["LegacyRoutes"], found_metals_as_string_array) + end + end + + def test_metals_should_return_alphabetical_list_of_found_metal_apps + use_appdir("multiplemetals") do + assert_equal(["MetalA", "MetalB"], found_metals_as_string_array) + end + end + + def test_metals_load_order_should_be_overriden_by_requested_metals + use_appdir("multiplemetals") do + Rails::Rack::Metal.requested_metals = ["MetalB", "MetalA"] + assert_equal(["MetalB", "MetalA"], found_metals_as_string_array) + end + end + + def test_metals_not_listed_should_not_load + use_appdir("multiplemetals") do + Rails::Rack::Metal.requested_metals = ["MetalB"] + assert_equal(["MetalB"], found_metals_as_string_array) + end + end + + def test_metal_finding_should_work_with_subfolders + use_appdir("subfolders") do + assert_equal(["Folder::MetalA", "Folder::MetalB"], found_metals_as_string_array) + end + end + + def test_metal_finding_with_requested_metals_should_work_with_subfolders + use_appdir("subfolders") do + Rails::Rack::Metal.requested_metals = ["Folder::MetalB"] + assert_equal(["Folder::MetalB"], found_metals_as_string_array) + end + end + + def test_metal_finding_should_work_with_multiple_metal_paths_in_185_and_below + use_appdir("singlemetal") do + engine_metal_path = "#{File.dirname(__FILE__)}/fixtures/plugins/engines/engine/app/metal" + Rails::Rack::Metal.metal_paths << engine_metal_path + $LOAD_PATH << engine_metal_path + assert_equal(["FooMetal", "EngineMetal"], found_metals_as_string_array) + end + end + + private + + def use_appdir(root) + dir = "#{File.dirname(__FILE__)}/fixtures/metal/#{root}" + Rails::Rack::Metal.metal_paths = ["#{dir}/app/metal"] + Rails::Rack::Metal.requested_metals = nil + $LOAD_PATH << "#{dir}/app/metal" + yield + end + + def found_metals_as_string_array + Rails::Rack::Metal.metals.map { |m| m.to_s } + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/mocks/routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/mocks/routes.rb new file mode 100644 index 00000000000..ea128636832 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/mocks/routes.rb @@ -0,0 +1,6 @@ +module ActionController + module Routing + class Routes + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_loader_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_loader_test.rb new file mode 100644 index 00000000000..b0ab4b77430 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_loader_test.rb @@ -0,0 +1,172 @@ +require 'plugin_test_helper' + +$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" +$:.unshift File.dirname(__FILE__) + "/../../actionmailer/lib" +require 'action_controller' +require 'action_mailer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +class TestPluginLoader < Test::Unit::TestCase + ORIGINAL_LOAD_PATH = $LOAD_PATH.dup + + def setup + reset_load_path! + + @configuration = Rails::Configuration.new + @configuration.plugin_paths << plugin_fixture_root_path + @initializer = Rails::Initializer.new(@configuration) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + + @failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + + @loader = Rails::Plugin::Loader.new(@initializer) + end + + def test_should_locate_plugins_by_asking_each_locator_specifed_in_configuration_for_its_plugins_result + locator_1 = stub(:plugins => [:a, :b, :c]) + locator_2 = stub(:plugins => [:d, :e, :f]) + locator_class_1 = stub(:new => locator_1) + locator_class_2 = stub(:new => locator_2) + @configuration.plugin_locators = [locator_class_1, locator_class_2] + assert_equal [:a, :b, :c, :d, :e, :f], @loader.send(:locate_plugins) + end + + def test_should_memoize_the_result_of_locate_plugins_as_all_plugins + plugin_list = [:a, :b, :c] + @loader.expects(:locate_plugins).once.returns(plugin_list) + assert_equal plugin_list, @loader.all_plugins + assert_equal plugin_list, @loader.all_plugins # ensuring that locate_plugins isn't called again + end + + def test_should_return_empty_array_if_configuration_plugins_is_empty + @configuration.plugins = [] + assert_equal [], @loader.plugins + end + + def test_should_find_all_availble_plugins_and_return_as_all_plugins + assert_plugins [ :engine, :stubby, :plugin_with_no_lib_dir, :gemlike, :acts_as_chunky_bacon, :a], @loader.all_plugins.reverse, @failure_tip + end + + def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_untouched + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip + end + + def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_nil + @configuration.plugins = nil + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip + end + + def test_should_return_specific_plugins_named_in_config_plugins_array_if_set + plugin_names = [:acts_as_chunky_bacon, :stubby] + only_load_the_following_plugins! plugin_names + assert_plugins plugin_names, @loader.plugins + end + + def test_should_respect_the_order_of_plugins_given_in_configuration + plugin_names = [:stubby, :acts_as_chunky_bacon] + only_load_the_following_plugins! plugin_names + assert_plugins plugin_names, @loader.plugins + end + + def test_should_load_all_plugins_in_natural_order_when_all_is_used + only_load_the_following_plugins! [:all] + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip + end + + def test_should_load_specified_plugins_in_order_and_then_all_remaining_plugins_when_all_is_used + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon, :all] + assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip + end + + def test_should_be_able_to_specify_loading_of_plugins_loaded_after_all + only_load_the_following_plugins! [:stubby, :all, :acts_as_chunky_bacon] + assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @loader.plugins, @failure_tip + end + + def test_should_accept_plugin_names_given_as_strings + only_load_the_following_plugins! ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir] + assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip + end + + def test_should_add_plugin_load_paths_to_global_LOAD_PATH_array + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + stubbed_application_lib_index_in_LOAD_PATHS = 4 + @loader.stubs(:application_lib_index).returns(stubbed_application_lib_index_in_LOAD_PATHS) + + @loader.add_plugin_load_paths + + assert $LOAD_PATH.index(File.join(plugin_fixture_path('default/stubby'), 'lib')) >= stubbed_application_lib_index_in_LOAD_PATHS + assert $LOAD_PATH.index(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) >= stubbed_application_lib_index_in_LOAD_PATHS + end + + def test_should_add_plugin_load_paths_to_Dependencies_load_paths + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + + @loader.add_plugin_load_paths + + assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) + assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) + end + + def test_should_add_engine_load_paths_to_Dependencies_load_paths + only_load_the_following_plugins! [:engine] + + @loader.add_plugin_load_paths + + %w( models controllers metal helpers ).each do |app_part| + assert ActiveSupport::Dependencies.load_paths.include?( + File.join(plugin_fixture_path('engines/engine'), 'app', app_part) + ), "Couldn't find #{app_part} in load path" + end + end + + def test_engine_controllers_and_action_mailers_should_have_their_view_path_set_when_loaded + only_load_the_following_plugins!([ :engine ]) + + @loader.send :add_engine_view_paths + + assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionController::Base.view_paths + assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionMailer::Base.view_paths + end + + def test_should_add_plugin_load_paths_to_Dependencies_load_once_paths + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + + @loader.add_plugin_load_paths + + assert ActiveSupport::Dependencies.load_once_paths.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) + assert ActiveSupport::Dependencies.load_once_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) + end + + def test_should_add_all_load_paths_from_a_plugin_to_LOAD_PATH_array + plugin_load_paths = ["a", "b"] + plugin = stub(:load_paths => plugin_load_paths) + @loader.stubs(:plugins).returns([plugin]) + + @loader.add_plugin_load_paths + + plugin_load_paths.each { |path| assert $LOAD_PATH.include?(path) } + end + + def test_should_add_locale_files_to_I18n_load_path + only_load_the_following_plugins! [:engine] + + @loader.send :add_engine_locales + + assert I18n.load_path.include?(File.join(plugin_fixture_path('engines/engine'), 'config', 'locales', 'en.yml')) + end + + + private + def reset_load_path! + $LOAD_PATH.clear + ORIGINAL_LOAD_PATH.each { |path| $LOAD_PATH << path } + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_locator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_locator_test.rb new file mode 100644 index 00000000000..471d9fc7c34 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_locator_test.rb @@ -0,0 +1,62 @@ +require 'plugin_test_helper' + +class PluginLocatorTest < Test::Unit::TestCase + def test_should_require_subclasses_to_implement_the_plugins_method + assert_raise(RuntimeError) do + Rails::Plugin::Locator.new(nil).plugins + end + end + + def test_should_iterator_over_plugins_returned_by_plugins_when_calling_each + locator = Rails::Plugin::Locator.new(nil) + locator.stubs(:plugins).returns([:a, :b, :c]) + plugin_consumer = mock + plugin_consumer.expects(:consume).with(:a) + plugin_consumer.expects(:consume).with(:b) + plugin_consumer.expects(:consume).with(:c) + + locator.each do |plugin| + plugin_consumer.consume(plugin) + end + end +end + +class PluginFileSystemLocatorTest < Test::Unit::TestCase + def setup + @configuration = Rails::Configuration.new + # We need to add our testing plugin directory to the plugin paths so + # the locator knows where to look for our plugins + @configuration.plugin_paths << plugin_fixture_root_path + @initializer = Rails::Initializer.new(@configuration) + @locator = Rails::Plugin::FileSystemLocator.new(@initializer) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + end + + def test_should_return_rails_plugin_instances_when_calling_create_plugin_with_a_valid_plugin_directory + assert_kind_of Rails::Plugin, @locator.send(:create_plugin, @valid_plugin_path) + end + + def test_should_return_nil_when_calling_create_plugin_with_an_invalid_plugin_directory + assert_nil @locator.send(:create_plugin, @empty_plugin_path) + end + + def test_should_return_all_plugins_found_under_the_set_plugin_paths + assert_equal ["a", "acts_as_chunky_bacon", "engine", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map(&:name).sort + end + + def test_should_find_plugins_only_under_the_plugin_paths_set_in_configuration + @configuration.plugin_paths = [File.join(plugin_fixture_root_path, "default")] + assert_equal ["acts_as_chunky_bacon", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map(&:name).sort + + @configuration.plugin_paths = [File.join(plugin_fixture_root_path, "alternate")] + assert_equal ["a"], @locator.plugins.map(&:name) + end + + def test_should_not_raise_any_error_and_return_no_plugins_if_the_plugin_path_value_does_not_exist + @configuration.plugin_paths = ["some_missing_directory"] + assert_nothing_raised do + assert @locator.plugins.empty? + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_test.rb new file mode 100644 index 00000000000..a6c390a45ab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_test.rb @@ -0,0 +1,162 @@ +require 'plugin_test_helper' + +class PluginTest < Test::Unit::TestCase + def setup + @initializer = Rails::Initializer.new(Rails::Configuration.new) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + @gemlike_plugin_path = plugin_fixture_path('default/gemlike') + end + + def test_should_determine_plugin_name_from_the_directory_of_the_plugin + assert_equal 'stubby', plugin_for(@valid_plugin_path).name + assert_equal 'empty', plugin_for(@empty_plugin_path).name + end + + def test_should_not_be_loaded_when_created + assert !plugin_for(@valid_plugin_path).loaded? + end + + def test_should_be_marked_as_loaded_when_load_is_called + plugin = plugin_for(@valid_plugin_path) + assert !plugin.loaded? + plugin.stubs(:evaluate_init_rb) + assert_nothing_raised do + plugin.send(:load, anything) + end + assert plugin.loaded? + end + + def test_should_determine_validity_of_given_path + # This is a plugin path, with a lib dir + assert plugin_for(@valid_plugin_path).valid? + # This just has an init.rb and no lib dir + assert plugin_for(plugin_fixture_path('default/plugin_with_no_lib_dir')).valid? + # This would be a plugin path, but the directory is empty + assert !plugin_for(plugin_fixture_path('default/empty')).valid? + # This is a non sense path + assert !plugin_for(plugin_fixture_path('default/this_directory_does_not_exist')).valid? + end + + def test_should_return_empty_array_for_load_paths_when_plugin_has_no_lib_directory + assert_equal [], plugin_for(plugin_fixture_path('default/plugin_with_no_lib_dir')).load_paths + end + + def test_should_return_array_with_lib_path_for_load_paths_when_plugin_has_a_lib_directory + expected_lib_dir = File.join(plugin_fixture_path('default/stubby'), 'lib') + assert_equal [expected_lib_dir], plugin_for(plugin_fixture_path('default/stubby')).load_paths + end + + def test_should_raise_a_load_error_when_trying_to_determine_the_load_paths_from_an_invalid_plugin + assert_nothing_raised do + plugin_for(@valid_plugin_path).load_paths + end + + assert_raise(LoadError) do + plugin_for(@empty_plugin_path).load_paths + end + + assert_raise(LoadError) do + plugin_for('this_is_not_a_plugin_directory').load_paths + end + end + + def test_should_raise_a_load_error_when_trying_to_load_an_invalid_plugin + # This path is fine so nothing is raised + assert_nothing_raised do + plugin = plugin_for(@valid_plugin_path) + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + + # This path is fine so nothing is raised + assert_nothing_raised do + plugin = plugin_for(@gemlike_plugin_path) + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + + # This is an empty path so it raises + assert_raise(LoadError) do + plugin = plugin_for(@empty_plugin_path) + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + + assert_raise(LoadError) do + plugin = plugin_for('this_is_not_a_plugin_directory') + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + end + + def test_should_raise_a_load_error_when_trying_to_access_load_paths_of_an_invalid_plugin + # This path is fine so nothing is raised + assert_nothing_raised do + plugin_for(@valid_plugin_path).load_paths + end + + # This is an empty path so it raises + assert_raise(LoadError) do + plugin_for(@empty_plugin_path).load_paths + end + + assert_raise(LoadError) do + plugin_for('this_is_not_a_plugin_directory').load_paths + end + end + + def test_loading_a_plugin_gives_the_init_file_access_to_all_it_needs + failure_tip = "Perhaps someone has written another test that loads this same plugin and therefore makes the StubbyMixin constant defined already." + assert !defined?(StubbyMixin), failure_tip + plugin = plugin_for(@valid_plugin_path) + plugin.load_paths.each { |path| $LOAD_PATH.unshift(path) } + # The init.rb of this plugin raises if it doesn't have access to all the things it needs + assert_nothing_raised do + plugin.load(@initializer) + end + assert defined?(StubbyMixin) + end + + def test_should_sort_naturally_by_name + a = plugin_for("path/a") + b = plugin_for("path/b") + z = plugin_for("path/z") + assert_equal [a, b, z], [b, z, a].sort + end + + def test_should_only_be_loaded_once + plugin = plugin_for(@valid_plugin_path) + assert !plugin.loaded? + plugin.expects(:evaluate_init_rb) + assert_nothing_raised do + plugin.send(:load, @initializer) + plugin.send(:load, @initializer) + end + assert plugin.loaded? + end + + def test_should_make_about_yml_available_as_about_method_on_plugin + plugin = plugin_for(@valid_plugin_path) + assert_equal "Plugin Author", plugin.about['author'] + assert_equal "1.0.0", plugin.about['version'] + end + + def test_should_return_empty_hash_for_about_if_about_yml_is_missing + assert_equal({}, plugin_for(about_yml_plugin_path('plugin_without_about_yaml')).about) + end + + def test_should_return_empty_hash_for_about_if_about_yml_is_malformed + assert_equal({}, plugin_for(about_yml_plugin_path('bad_about_yml')).about) + end + + private + + def about_yml_plugin_path(name) + File.join(File.dirname(__FILE__), 'fixtures', 'about_yml_plugins', name) + end + + def plugin_for(path) + Rails::Plugin.new(path) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_test_helper.rb new file mode 100644 index 00000000000..f8c094d19f8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/plugin_test_helper.rb @@ -0,0 +1,29 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" +$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" + +require 'test/unit' +require 'active_support' +require 'initializer' +require File.join(File.dirname(__FILE__), 'abstract_unit') + +# We need to set RAILS_ROOT if it isn't already set +RAILS_ROOT = '.' unless defined?(RAILS_ROOT) + +class Test::Unit::TestCase + private + def plugin_fixture_root_path + File.join(File.dirname(__FILE__), 'fixtures', 'plugins') + end + + def only_load_the_following_plugins!(plugins) + @initializer.configuration.plugins = plugins + end + + def plugin_fixture_path(path) + File.join(plugin_fixture_root_path, path) + end + + def assert_plugins(list_of_names, array_of_plugins, message=nil) + assert_equal list_of_names.map(&:to_s), array_of_plugins.map(&:name), message + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rack_static_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rack_static_test.rb new file mode 100644 index 00000000000..ad673f6f19a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rack_static_test.rb @@ -0,0 +1,46 @@ +require 'abstract_unit' + +require 'action_controller' +require 'rails/rack' + +class RackStaticTest < ActiveSupport::TestCase + def setup + FileUtils.cp_r "#{RAILS_ROOT}/fixtures/public", "#{RAILS_ROOT}/public" + end + + def teardown + FileUtils.rm_rf "#{RAILS_ROOT}/public" + end + + DummyApp = lambda { |env| + [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] + } + App = Rails::Rack::Static.new(DummyApp) + + test "serves dynamic content" do + assert_equal "Hello, World!", get("/nofile") + end + + test "serves static index at root" do + assert_equal "/index.html", get("/index.html") + assert_equal "/index.html", get("/index") + assert_equal "/index.html", get("/") + end + + test "serves static file in directory" do + assert_equal "/foo/bar.html", get("/foo/bar.html") + assert_equal "/foo/bar.html", get("/foo/bar/") + assert_equal "/foo/bar.html", get("/foo/bar") + end + + test "serves static index file in directory" do + assert_equal "/foo/index.html", get("/foo/index.html") + assert_equal "/foo/index.html", get("/foo/") + assert_equal "/foo/index.html", get("/foo") + end + + private + def get(path) + Rack::MockRequest.new(App).request("GET", path).body + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_generator_test.rb new file mode 100644 index 00000000000..b2fc2f585de --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_generator_test.rb @@ -0,0 +1,145 @@ +require 'test/unit' + +# Optionally load RubyGems. +begin + require 'rubygems' +rescue LoadError +end + +# Mock out what we need from AR::Base. +module ActiveRecord + class Base + class << self + attr_accessor :pluralize_table_names + end + self.pluralize_table_names = true + end +end + +# And what we need from ActionView +module ActionView + module Helpers + module ActiveRecordHelper; end + class InstanceTag; end + end +end + + +# Must set before requiring generator libs. +if defined?(RAILS_ROOT) + RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures" +else + RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures" +end + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +require 'initializer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +require 'rails_generator' + +class RailsGeneratorTest < Test::Unit::TestCase + BUILTINS = %w(controller integration_test mailer migration model observer plugin resource scaffold session_migration) + CAPITALIZED_BUILTINS = BUILTINS.map { |b| b.capitalize } + + def setup + ActiveRecord::Base.pluralize_table_names = true + end + + def test_sources + expected = [:lib, :vendor, + "plugins (vendor/plugins)".to_sym, # /generators and /rails_generators + :user, + :RubyGems, :RubyGems, # gems named _generator, gems containing /rails_generator/ folder + :builtin] + expected.delete(:RubyGems) unless Object.const_defined?(:Gem) + assert_equal expected, Rails::Generator::Base.sources.map { |s| s.label } + end + + def test_lookup_builtins + (BUILTINS + CAPITALIZED_BUILTINS).each do |name| + assert_nothing_raised do + spec = Rails::Generator::Base.lookup(name) + assert_not_nil spec + assert_kind_of Rails::Generator::Spec, spec + + klass = spec.klass + assert klass < Rails::Generator::Base + assert_equal spec, klass.spec + end + end + end + + def test_autolookup + assert_nothing_raised { ControllerGenerator } + assert_nothing_raised { ModelGenerator } + end + + def test_lookup_missing_generator + assert_raise Rails::Generator::GeneratorError do + Rails::Generator::Base.lookup('missing').klass + end + end + + def test_lookup_missing_class + spec = nil + assert_nothing_raised { spec = Rails::Generator::Base.lookup('missing_class') } + assert_not_nil spec + assert_kind_of Rails::Generator::Spec, spec + assert_raise(NameError) { spec.klass } + end + + def test_generator_usage + (BUILTINS - ["session_migration"]).each do |name| + assert_raise(Rails::Generator::UsageError, "Generator '#{name}' should raise an error without arguments") { + Rails::Generator::Base.instance(name) + } + end + end + + def test_generator_spec + spec = Rails::Generator::Base.lookup('working') + assert_equal 'working', spec.name + assert_match(/#{spec.path}$/, "#{RAILS_ROOT}/lib/generators/working") + assert_equal :lib, spec.source + assert_nothing_raised { assert_match(/WorkingGenerator$/, spec.klass.name) } + end + + def test_named_generator_attributes + g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz)) + assert_equal 'admin/foo', g.name + assert_equal %w(admin), g.class_path + assert_equal 'Admin', g.class_nesting + assert_equal 'Admin::Foo', g.class_name + assert_equal 'foo', g.singular_name + assert_equal 'foos', g.plural_name + assert_equal g.singular_name, g.file_name + assert_equal "admin_#{g.plural_name}", g.table_name + assert_equal %w(bar baz), g.args + end + + def test_named_generator_attributes_without_pluralized + ActiveRecord::Base.pluralize_table_names = false + g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz)) + assert_equal "admin_#{g.singular_name}", g.table_name + end + + def test_session_migration_generator_with_pluralization + g = Rails::Generator::Base.instance('session_migration') + assert_equal 'session'.pluralize, g.send(:default_session_table_name) + ActiveRecord::Base.pluralize_table_names = false + assert_equal 'session', g.send(:default_session_table_name) + end + + def test_scaffold_controller_name + # Default behaviour is use the model name + g = Rails::Generator::Base.instance('scaffold', %w(Product)) + assert_equal "Products", g.controller_name + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_info_controller_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_info_controller_test.rb new file mode 100644 index 00000000000..e274e1aa6e1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_info_controller_test.rb @@ -0,0 +1,52 @@ +require 'abstract_unit' +require 'action_controller' +require 'action_controller/test_process' + +module Rails; end +require 'rails/info' +require 'rails/info_controller' + +class Rails::InfoController < ActionController::Base + @local_request = false + class << self + cattr_accessor :local_request + end + + # Re-raise errors caught by the controller. + def rescue_action(e) raise e end; + +protected + def local_request? + self.class.local_request + end +end + +ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' +end + +class Rails::InfoControllerTest < ActionController::TestCase + def setup + @controller = Rails::InfoController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + ActionController::Base.consider_all_requests_local = true + end + + def test_rails_info_properties_table_rendered_for_local_request + Rails::InfoController.local_request = true + get :properties + assert_tag :tag => 'table' + assert_response :success + end + + def test_rails_info_properties_error_rendered_for_non_local_request + Rails::InfoController.local_request = false + ActionController::Base.consider_all_requests_local = false + + get :properties + assert_tag :tag => 'p' + assert_response 500 + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_info_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_info_test.rb new file mode 100644 index 00000000000..9befd44a58b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/rails_info_test.rb @@ -0,0 +1,99 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" +$:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" +$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" + +require 'test/unit' +require 'active_support' + +unless defined?(Rails) && defined?(Rails::Info) + module Rails + class Info; end + end +end + +class InfoTest < Test::Unit::TestCase + def setup + Rails.send :remove_const, :Info + silence_warnings { load 'rails/info.rb' } + end + + def test_edge_rails_revision_not_set_when_svn_info_is_empty + Rails::Info.property 'Test that this will not be defined' do + Rails::Info.edge_rails_revision '' + end + assert !property_defined?('Test that this will not be defined') + end + + def test_edge_rails_revision_extracted_from_svn_info + Rails::Info.property 'Test Edge Rails revision' do + Rails::Info.edge_rails_revision <<-EOS + commit 420c4b3d8878156d04f45e47050ddc62ae00c68c + Author: David Heinemeier Hansson + Date: Sun Apr 13 17:33:27 2008 -0500 + + Added Rails.public_path to control where HTML and assets are expected to be loaded from +EOS + end + + assert_property 'Test Edge Rails revision', '420c4b3d8878156d04f45e47050ddc62ae00c68c' + end + + def test_property_with_block_swallows_exceptions_and_ignores_property + assert_nothing_raised do + Rails::Info.module_eval do + property('Bogus') {raise} + end + end + assert !property_defined?('Bogus') + end + + def test_property_with_string + Rails::Info.module_eval do + property 'Hello', 'World' + end + assert_property 'Hello', 'World' + end + + def test_property_with_block + Rails::Info.module_eval do + property('Goodbye') {'World'} + end + assert_property 'Goodbye', 'World' + end + + def test_framework_version + assert_property 'Active Support version', ActiveSupport::VERSION::STRING + end + + def test_frameworks_exist + Rails::Info.frameworks.each do |framework| + dir = File.dirname(__FILE__) + "/../../" + framework.gsub('_', '') + assert File.directory?(dir), "#{framework.classify} does not exist" + end + end + + protected + def svn_info=(info) + Rails::Info.module_eval do + class << self + def svn_info + info + end + end + end + end + + def properties + Rails::Info.properties + end + + def property_defined?(property_name) + properties.names.include? property_name + end + + def assert_property(property_name, value) + raise "Property #{property_name.inspect} not defined" unless + property_defined? property_name + assert_equal value, properties.value_for(property_name) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/secret_key_generation_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/secret_key_generation_test.rb new file mode 100644 index 00000000000..2c7c3d5dfe2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/secret_key_generation_test.rb @@ -0,0 +1,38 @@ +require 'abstract_unit' + +# Must set before requiring generator libs. +if defined?(RAILS_ROOT) + RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures" +else + RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures" +end + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" + +require 'initializer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +require 'rails_generator' +require 'rails_generator/secret_key_generator' +require 'rails_generator/generators/applications/app/app_generator' + +class SecretKeyGenerationTest < ActiveSupport::TestCase + SECRET_KEY_MIN_LENGTH = 128 + APP_NAME = "foo" + + def setup + @generator = Rails::SecretKeyGenerator.new(APP_NAME) + end + + def test_secret_key_generation + assert_deprecated /ActiveSupport::SecureRandom\.hex\(64\)/ do + assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification new file mode 100644 index 00000000000..86dba2092c7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-a +version: !ruby/object:Gem::Version + version: 0.4.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-a.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem A diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb new file mode 100644 index 00000000000..0453b38ab83 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb @@ -0,0 +1 @@ +DUMMY_GEM_A_VERSION="0.4.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification new file mode 100644 index 00000000000..5ea692d7a10 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-b +version: !ruby/object:Gem::Version + version: 0.4.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-b.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem B diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb new file mode 100644 index 00000000000..850b5dda839 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb @@ -0,0 +1 @@ +DUMMY_GEM_B_VERSION="0.4.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification new file mode 100644 index 00000000000..ab4707124af --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-b +version: !ruby/object:Gem::Version + version: 0.6.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-b.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem B diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb new file mode 100644 index 00000000000..7d6d01cd48f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb @@ -0,0 +1 @@ +DUMMY_GEM_B_VERSION="0.6.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification new file mode 100644 index 00000000000..f90f60424ce --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-c +version: !ruby/object:Gem::Version + version: 0.4.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-c.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem C diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb new file mode 100644 index 00000000000..1a416bef82a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb @@ -0,0 +1 @@ +DUMMY_GEM_C_VERSION="0.4.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification new file mode 100644 index 00000000000..e75c0aa66ac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-c +version: !ruby/object:Gem::Version + version: 0.6.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-c.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem C diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb new file mode 100644 index 00000000000..9ba2ca8bb50 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb @@ -0,0 +1 @@ +DUMMY_GEM_C_VERSION="0.6.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb new file mode 100644 index 00000000000..e5cb007e5f8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb @@ -0,0 +1 @@ +DUMMY_GEM_D_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification new file mode 100644 index 00000000000..ce4443c8be3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-e +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-e.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem E diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb new file mode 100644 index 00000000000..48bf91a7014 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb @@ -0,0 +1 @@ +DUMMY_GEM_E_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification new file mode 100644 index 00000000000..70a36b9a8c1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification @@ -0,0 +1,39 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-f +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: absolutely-no-such-gem + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-f.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem F diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb new file mode 100644 index 00000000000..0271c8c48a8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb @@ -0,0 +1 @@ +DUMMY_GEM_F_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification new file mode 100644 index 00000000000..27e29912a67 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification @@ -0,0 +1,39 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-g +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-f + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-g.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb new file mode 100644 index 00000000000..8fc056586cd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb @@ -0,0 +1 @@ +DUMMY_GEM_G_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification new file mode 100644 index 00000000000..b3f7930948b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification @@ -0,0 +1,29 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-h +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +files: +- lib +- lib/dummy-gem-h.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem H diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb new file mode 100644 index 00000000000..0f91234936b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb @@ -0,0 +1 @@ +DUMMY_GEM_H_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification new file mode 100644 index 00000000000..50b4969da50 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification @@ -0,0 +1,41 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-i +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-i + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +extensions: +- ext/dummy-gem-i/extconf.rb +files: +- lib +- lib/dummy-gem-i.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb new file mode 100644 index 00000000000..2f9a376c2c8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb @@ -0,0 +1 @@ +DUMMY_GEM_I_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification new file mode 100644 index 00000000000..2c456546fc5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification @@ -0,0 +1,41 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-j +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-j + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +extensions: +- ext/dummy-gem-j/extconf.rb +files: +- lib +- lib/dummy-gem-j.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb new file mode 100644 index 00000000000..8ecd363ff89 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb @@ -0,0 +1 @@ +DUMMY_GEM_J_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification new file mode 100644 index 00000000000..20edd0f8564 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification @@ -0,0 +1,49 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-k +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-k + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +- !ruby/object:Gem::Dependency + name: dummy-gem-h + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-k.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem I diff --git a/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb new file mode 100644 index 00000000000..97fb1d69ce8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/rails/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb @@ -0,0 +1 @@ +DUMMY_GEM_K_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/CHANGELOG b/crowbar/change-image/dell/openstack_manager/vendor/railties/CHANGELOG new file mode 100644 index 00000000000..a94249b6991 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/CHANGELOG @@ -0,0 +1,2172 @@ +*Edge* + +* Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs [DHH] + +* 1.9 compatibility + +*2.3.4 (September 4, 2009)* + +* I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs] + + +*2.3.3 (July 12 2009) + +* Version bump + + +*2.3.2 [Final] (March 15, 2009)* + +* Allow metal to live in plugins #2045 [Matthew Rudy] + +* Added metal [Josh Peek] + +* Remove script/performance/request in favour of the performance integration tests. [Pratik Naik] + + To continue using script/performance/request, install the request_profiler plugin : + + script/plugin install git://github.com/rails/request_profiler.git + +* Add a rake task to apply a template to an existing application : rake rails:template LOCATION=~/template.rb [Pratik Naik] + +* Add "-m/--template" option to Rails generator to apply a template to the generated application. [Jeremy McAnally] + + This has been extracted from rg - http://github.com/jeremymcanally/rg + + Example: + + # template.rb + + # Install plugins from git or svn + plugin "will-paginate", :git => "git://github.com/mislav/will_paginate.git" + plugin "old-restful-auth", :svn => "http://svn.techno-weenie.net/projects/plugins/restful_authentication/" + + # Add gems to environment.rb + gem "jeremymcanally-context" + gem "bluecloth" + + # Vendor file. Data in a string or... + vendor("borrowed.rb", < -m /path/to/my/template.rb + + 2. Or directly from a URL : + + rails --template=http://gist.github.com/31208.txt + +* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts [David Heinemeier Hansson] + +* Changed Rails.root to return a Pathname object (allows for Rails.root.join('app', 'controllers') => "#{RAILS_ROOT}/app/controllers") #1482 [Damian Janowski/?] + +* Added view path support for engines [David Heinemeier Hansson] + +* Added that config/routes.rb files in engine plugins are automatically loaded (and reloaded when they change in dev mode) [David Heinemeier Hansson] + +* Added app/[models|controllers|helpers] to the load path for plugins that has an app directory (go engines ;)) [David Heinemeier Hansson] + +* Add config.preload_frameworks to load all frameworks at startup. Default to false so Rails autoloads itself as it's used. Turn this on for Passenger and JRuby. Also turned on by config.threadsafe! [Jeremy Kemper] + +* Add a rake task to generate dispatchers : rake rails:generate_dispatchers [Pratik Naik] + +* "rails " will not generate public/dispatch.cgi/fcgi/rb files by default now. Please use "--with-dispatchers" option if you need them. [Yaroslav Markin, Pratik Naik] + +* Added rake rails:update:application_controller to renamed application.rb to application_controller.rb -- included in rake rails:update so upgrading to 2.3 will automatically trigger it #1439 [kastner] + +* Added Rails.backtrace_cleaner as an accessor for the Rails::BacktraceCleaner instance used by the framework to cut down on backtrace noise and config/initializers/backtrace_silencers.rb to add your own (or turn them all off) [David Heinemeier Hansson] + +* Switch from Test::Unit::TestCase to ActiveSupport::TestCase. [Jeremy Kemper] + +* Added config.i18n settings gatherer to config/environment, auto-loading of all locales in config/locales/*.rb,yml, and config/locales/en.yml as a sample locale [David Heinemeier Hansson] + +* BACKWARDS INCOMPATIBLE: Renamed application.rb to application_controller.rb and removed all the special casing that was in place to support the former. You must do this rename in your own application when you upgrade to this version [David Heinemeier Hansson] + + +*2.2.1 [RC2] (November 14th, 2008)* + +* Fixed plugin generator so that generated unit tests would subclass ActiveSupport::TestCase, also introduced a helper script to reduce the needed require statements #1137 [Mathias Meyer] + +* Update Prototype to 1.6.0.3 [sam] + + +*2.2.0 [RC1] (October 24th, 2008)* + +* Fixed that sqlite would report "db/development.sqlite3 already exists" whether true or not on db:create #614 [Antonio Cangiano] + +* Added config.threadsafe! to toggle allow concurrency settings and disable the dependency loader [Josh Peek] + +* Turn cache_classes on by default [Josh Peek] + +* Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek] + +* Introduce simple internationalization support. [Ruby i18n team] + +* Make script/plugin install -r option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example: + + script/plugin install git://github.com/mislav/will_paginate.git -r agnostic # Installs 'agnostic' branch + script/plugin install git://github.com/dchelimsky/rspec.git -r 'tag 1.1.4' + +* Added Rails.initialized? flag [Josh Peek] + +* Make rake test:uncommitted work with Git. [Tim Pope] + +* Added Thin support to script/server. #488 [Bob Klosinski] + +* Fix script/about in production mode. #370 [Cheah Chu Yeow, Xavier Noria, David Krmpotic] + +* Add the gem load paths before the framework is loaded, so certain gems like RedCloth and BlueCloth can be frozen. + +* Fix discrepancies with loading rails/init.rb from gems. + +* Plugins check for the gem init path (rails/init.rb) before the standard plugin init path (init.rb) [Jacek Becela] + +* Changed all generated tests to use the test/do declaration style [David Heinemeier Hansson] + +* Wrapped Rails.env in StringInquirer so you can do Rails.env.development? [David Heinemeier Hansson] + +* Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) [#310 state:resolved] + + +*2.1.0 (May 31st, 2008)* + +* script/dbconsole fires up the command-line database client. #102 [Steve Purcell] + +* Fix bug where plugin init.rb files from frozen gem specs weren't being run. (pjb3) [#122 state:resolved] + +* Made the location of the routes file configurable with config.routes_configuration_file (Scott Fleckenstein) [#88] + +* Rails Edge info returns the latest git commit hash [Francesc Esplugas] + +* Added Rails.public_path to control where HTML and assets are expected to be loaded from (defaults to Rails.root + "/public") #11581 [Nick Sieger] + +* rake time:zones:local finds correct base utc offset for zones in the Southern Hemisphere [Geoff Buesing] + +* Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. [Rick Olson] + +* script/performance/profiler compatibility with the ruby-prof >= 0.5.0. Closes #9176. [Jonathan del Strother] + +* Flesh out rake gems:unpack to unpack all gems, and add rake gems:build for native extensions. #11513 [ddollar] + + rake gems:unpack # unpacks all gems + rake gems:unpack GEM=mygem # unpacks only the gem 'mygem' + + rake gems:build # builds all unpacked gems + rake gems:build GEM=mygem # builds only the gem 'mygem' + +* Add config.active_support for future configuration options. Also, add more new Rails 3 config settings to new_rails_defaults.rb [Rick Olson] + +* Add Rails.logger, Rails.root, Rails.env and Rails.cache shortcuts for RAILS_* constants [Pratik Naik] + +* Allow files in plugins to be reloaded like the rest of the application. [Rick Olson] + + Enables or disables plugin reloading. + + config.reload_plugins = true + + You can get around this setting per plugin. + If #reload_plugins? == false (DEFAULT), add this to your plugin's init.rb to make it reloadable: + + Dependencies.load_once_paths.delete lib_path + + If #reload_plugins? == true, add this to your plugin's init.rb to only load it once: + + Dependencies.load_once_paths << lib_path + +* Small tweak to allow plugins to specify gem dependencies. [Rick Olson] + + # OLD open_id_authentication plugin init.rb + require 'yadis' + require 'openid' + ActionController::Base.send :include, OpenIdAuthentication + + # NEW + config.gem "ruby-openid", :lib => "openid", :version => "1.1.4" + config.gem "ruby-yadis", :lib => "yadis", :version => "0.3.4" + + config.after_initialize do + ActionController::Base.send :include, OpenIdAuthentication + end + +* Added config.gem for specifying which gems are required by the application, as well as rake tasks for installing and freezing gems. [Rick Olson] + + Rails::Initializer.run do |config| + config.gem "bj" + config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" + config.gem "aws-s3", :lib => "aws/s3" + end + + # List required gems. + rake gems + + # Install all required gems: + rake gems:install + + # Unpack specified gem to vendor/gems/gem_name-x.x.x + rake gems:unpack GEM=bj + +* Removed the default .htaccess configuration as there are so many good deployment options now (kept it as an example in README) [David Heinemeier Hansson] + +* config.time_zone accepts TZInfo::Timezone identifiers as well as Rails TimeZone identifiers [Geoff Buesing] + +* Rails::Initializer#initialize_time_zone raises an error if value assigned to config.time_zone is not recognized. Rake time zone tasks only require ActiveSupport instead of entire environment [Geoff Buesing] + +* Stop adding the antiquated test/mocks/* directories and only add them to the path if they're still there for legacy reasons [David Heinemeier Hansson] + +* Added that gems can now be plugins if they include rails/init.rb #11444 [John Barnette] + +* Added Plugin#about method to programmatically access the about.yml in a plugin #10979 [James Adam] + + plugin = Rails::Plugin.new(path_to_my_plugin) + plugin.about["author"] # => "James Adam" + plugin.about["url"] # => "http://interblah.net" + +* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert] + +* Added config.time_zone = 'UTC' in the default environment.rb [Geoff Buesing] + +* Added rake tasks time:zones:all, time:zones:us and time:zones:local for finding time zone names for config.time_zone option [Geoff Buesing] + +* Add config.time_zone for configuring the default Time.zone value. #10982 [Geoff Buesing] + +* Added support for installing plugins hosted at git repositories #11294 [Jack Danger Canty] + +* Fixed that script/generate would not look for plugin generators in plugin_paths #11000 [glv] + +* Fixed database rake tasks to work with charset/collation and show proper error messages on failure. Closes #11301 [matt] + +* Added a -e/--export to script/plugin install, uses svn export. #10847 [jon@blankpad.net)] + +* Reshuffle load order so that routes and observers are initialized after plugins and app initializers. Closes #10980 [Rick Olson] + +* Git support for script/generate. #10690 [ssoroka] + +* Update scaffold to use labels instead of bold tags. Closes #10757 [zach-inglis-lt3] + +* Resurrect WordNet synonym lookups. #10710 [tom./, matt] + +* Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) [David Heinemeier Hansson] + +* Added that rails:update is run when you do rails:freeze:edge to ensure you also get the latest JS and config files #10565 [jeff] + +* SQLite: db:drop:all doesn't fail silently if the database is already open. #10577 [Cheah Chu Yeow, mrichman] + +* Introduce native mongrel handler and push mutex into dispatcher. [Jeremy Kemper] + +* Ruby 1.9 compatibility. #1689, #10546 [Cheah Chu Yeow, frederico] + + +*2.0.2* (December 16th, 2007) + +* Changed the default database from mysql to sqlite3, so now running "rails myapp" will have a config/database.yml that's setup for SQLite3 (which in OS X Leopard is installed by default, so is the gem, so everything Just Works with no database configuration at all). To get a Rails application preconfigured for MySQL, just run "rails -d mysql myapp" [David Heinemeier Hansson] + +* Turned on ActionView::Base.cache_template_loading by default in config/environments/production.rb to prevent file system stat calls for every template loading to see if it changed (this means that you have to restart the application to see template changes in production mode) [David Heinemeier Hansson] + +* Introduce `rake secret` to output a crytographically secure secret key for use with cookie sessions #10363 [revans] + +* Fixed that local database creation should consider 127.0.0.1 local #9026 [parcelbrat] + +* Fixed that functional tests generated for scaffolds should use fixture calls instead of hard-coded IDs #10435 [boone] + +* Added db:migrate:redo and db:migrate:reset for rerunning existing migrations #10431, #10432 [matt] + +* RAILS_GEM_VERSION may be double-quoted also. #10443 [James Cox] + +* Update rails:freeze:gems to work with RubyGems 0.9.5. [Jeremy Kemper] + + +*2.0.1* (December 7th, 2007) + +* Fixed Active Record bug + + +*2.0.0* (December 6th, 2007) + +* The test task stops with a warning if you have pending migrations. #10377 [Josh Knowles] + +* Add warning to documentation about using transactional fixtures when the code under test uses transactions itself. Closes #7548 [Thijs van der Vossen] + +* Update Prototype to 1.6.0.1. [sam] + +* Update script.aculo.us to 1.8.0.1. [madrobby] + +* Added db:fixtures:identity as a way of locating what ID a foxy fixture was assigned #10332 [John Barnette] + +* Generated fixtures should not specify ids since theyre expected to be foxy fixtures #10330 [John Barnette] + +* Update to Prototype -r8232. [sam] + +* Introduce SecretKeyGenerator for more secure session secrets than CGI::Session's pseudo-random id generator. Consider extracting to Active Support later. #10286 [Hongli Lai (Phusion)] + +* RAILS_GEM_VERSION may be set to any valid gem version specifier. #10057 [Chad Woolley, Cheah Chu Yeow] + +* Load config/preinitializer.rb, if present, before loading the environment. #9943 [Chad Woolley] + +* FastCGI handler ignores unsupported signals like USR2 on Windows. [Grzegorz Derebecki] + +* Only load ActionMailer::TestCase if ActionMailer is loaded. Closes #10137 [defunkt] + +* Fixed that db:reset would use migrations instead of loading db/schema.rb [David Heinemeier Hansson] + +* Ensure the plugin loader only loads plugins once. Closes #10102 [haruki_zaemon] + +* Refactor Plugin Loader. Add plugin lib paths early, and add lots of tests. Closes #9795 [James Adam] + +* Added --skip-timestamps to generators that produce models #10036 [Tim Pope] + +* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. [sam, madrobby] + +* Added db:rollback to rollback the schema one version (or multiple as specified by STEP) [Jeffrey Allan Hardy] + +* Fix typo in test_helper. Closes #9925 [viktor tron] + +* Request profiler. [Jeremy Kemper] + +* config/boot.rb correctly detects RAILS_GEM_VERSION. #9834 [alexch, thewoolleyman] + +* Fixed incorrect migration number if script/generate executed outside of Rails root #7080 [Jeremy McAnally] + +* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0. [sam, madrobby] + +* Generated fixtures use the actual primary key instead of id. #4343 [Frederick Ros, Tarmo Tänav] + +* Extend the console +helper+ method to allow you to include custom helpers. e.g: + >> helper :posts + >> helper.some_method_from_posts_helper(Post.find(1)) + +* db:create works with remote databases whereas db:create:all only creates +databases on localhost. #9753 [Trevor Wennblom] + +* Removed calls to fixtures in generated tests as fixtures :all is now present by default in test_helper.rb [David Heinemeier Hansson] + +* Add --prefix option to script/server when using mongrel. [dacat] + + +*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.1.4 - 1.2.3] + +* Fixed that installing plugins from SVN repositories that use trunk/ will work #8188 [evan] + +* Moved the SourceAnnotationExtractor to a separate file in case libraries try to load the rails rake tasks twice. [Rick Olson] + +* Moved Dispatcher to ActionController::Dispatcher. [Jeremy Kemper] + +* Changed the default logger from Ruby's own Logger with the clean_logger extensions to ActiveSupport::BufferedLogger for performance reasons [David Heinemeier Hansson]. (You can change it back with config.logger = Logger.new("/path/to/log", level).) + +* Added a default 422.html page to be rendered when ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, or ActionController::InvalidAuthenticityToken is raised [David Heinemeier Hansson] + +* Added --skip-fixture option to script/generate model #6862 [sandofsky] + +* Print Rails version when starting console #7440 [eyematz] + +* Fixed the placement of fixture files for nested models when generating through script/generate model #7547 [jkit] + +* Added TEMPLATE option to rake doc:app to set a custom output template #7737 [Jakob Skjerning] + +* Added VERBOSE option to rake db:migrate to turn off output #8204 [John Barnette] + +* Fixed that rake doc:app should use UTF-8 #8906 [farzy] + +* Fixes rake annotations to search erb and builder files as well #9150 [m.langenberg] + +* Removed web_service generator [Michael Koziarski] + +* Added the :all option to config.plugins that'll include the rest of the plugins not already explicitly named #9613 [Frederick Cheung]. Example: + + # Loads :classic_pagination before all the other plugins + config.plugins = [ :classic_pagination, :all ] + +* Added symbols as a legal way of specifying plugins in config.plugins #9629 [tom] + +* Removed deprecated task names, like clear_logs, in favor of the new namespaced style [David Heinemeier Hansson] + +* Support multiple config.after_initialize blocks so plugins and apps can more easily cooperate. #9582 [Zach Dennis] + +* Added db:drop:all to drop all databases declared in config/database.yml [David Heinemeier Hansson] + +* Use attribute pairs instead of the migration name to create add and remove column migrations. Closes #9166 [Pratik Naik] + + For example: + + ruby script/generation migration AddSomeStuffToCustomers first_name:string last_name:string + + or + + ruby script/generation migration RemoveSomeStuffFromCustomers first_name:string last_name:string + +* Add ActiveResource to Rails::Info. Closes #8741 [Chris Kampmeier] + +* use Gem.find_name instead of search when freezing gems. Prevent false positives for other gems with rails in the name. Closes #8729 [wselman] + +* Automatically generate add/remove column commands in specially named migrations like AddLocationToEvent. Closes #9006 [Ryan Davis] + +* Default to plural table name in Rails Generator if ActiveRecord is not present. Closes #8963 [evan] + +* Added rake routes for listing all the defined routes in the system. #8795 [Josh Peek] + +* db:create creates the database for the current environment if it's on localhost. db:create:all creates local databases for all environments. #8783 [matt] + +* Generators: look for generators in all gems, not just those suffixed with _generator, in the gem's generators or rails_generators directory. Allow use of the rails_generators directory instead of the standard generators directory in plugins also. #8730 [Dr Nic, topfunky] + +* MySQL, PostgreSQL: database.yml defaults to utf-8. #8701 [matt] + +* Added db:version to get the current schema number [via Err The Blog] + +* Added --skip-migration option to scaffold and resource generators #8656 [Michael Glaesemann] + +* Fix that FCGIs would leave log files open when asked to shut down by USR2. #3028 [Sebastian Kanthak, Josh Peek] + +* Fixed that dispatcher preparation callbacks only run once in production mode. Mock Routes.reload so that dispatcher preparation callback tests run. [Rick Olson] + +* Fix syntax error in dispatcher than wrecked failsafe responses. #8625 [mtitorenko] + +* Scaffolded validation errors set the appropriate HTTP status for XML responses. #6946, #8622 [Manfred Stienstra, mmmultiworks] + +* Sexy migrations for the session_migration generator. #8561 [Vladislav] + +* Console reload! runs to_prepare callbacks also. #8393 [f.svehla] + +* Generated migrations include timestamps by default. #8501 [Shane Vitarana] + +* Drop Action Web Service from rails:freeze:edge. [Jeremy Kemper] + +* Add db:create, drop, reset, charset, and collation tasks. #8448 [matt] + +* Scaffold generator depends on model generator instead of duplicating it. #7222 [bscofield] + +* Scaffold generator tests. #8443 [pelle] + +* Generated scaffold functional tests use assert_difference. #8421 [Norbert Crombach] + +* Update to Prototype 1.5.1. [Sam Stephenson] + +* Update to script.aculo.us 1.7.1_beta3. [Thomas Fuchs] + +* Generators use *.html.erb view template naming. #8278 [Tim Pope] + +* Updated resource_scaffold and model generators to use short-hand style migrations [David Heinemeier Hansson] + +* Updated initializer to only load #{RAILS_ENV}.rb once. Added deprecation warning for config.breakpoint_server. [Nicholas Seckar] + +* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [David Heinemeier Hansson] + + To use the new debugger, start your server with script/server --debugger and insert a call to 'debugger' + (instead of 'breakpoint') where you want to jump into the debugger. + + BACKWARDS INCOMPATIBILITY NOTE: You must remove the default line 12 from config/environments/development.rb: + + config.breakpoint_server = true + + This configuration option is no longer available. Rails will fail to start in development mode as long as + that's still present. + +* Resource scaffolding returns the created entity.to_xml. [Jeremy Kemper] + +* Resource scaffolding responds to new.xml. #8185 [Eric Mill] + +* Include Active Resource in rails:freeze:edge rake task. [Thomas Fuchs] + +* Include Active Resource instead of Action Web Service [David Heinemeier Hansson] You can add AWS back with this in config/environment.rb: + + config.load_paths += %W( #{RAILS_ROOT}/vendor/rails/actionwebservice/lib ) + + ...or just gem 'actionwebservice' + +* Give generate scaffold a more descriptive database message. Closes #7316 [Jeremy McAnally] + +* Canonicalize RAILS_ROOT by using File.expand_path on Windows, which doesn't have to worry about symlinks, and Pathname#realpath elsewhere, which respects symlinks in relative paths but is incompatible with Windows. #6755 [Jeremy Kemper, trevor] + +* Deprecation: remove components from controller paths. [Jeremy Kemper] + +* Add environment variable RAILS_DEFAULT_DATABASE, which allows the builtin default of 'mysql' to be overridden. [Nicholas Seckar] + +* Windows: include MinGW in RUBY_PLATFORM check. #2982 [okkez000@gmail.com, Kaspar Schiess] + +* Split out the basic plugin locator functionality into an abstract super class. Add a FileSystemLocator to do the job of checking the plugin_paths for plugins. Add plugin_locators configuration option which will iterate over the set of plugin locators and load each of the plugin loaders they return. Rename locater everywhere to locator. [Marcel Molina Jr.] + +* Split plugin location and loading out of the initializer and into a new Plugin namespace, which includes Plugin::Locater and Plugin::Loader. The loader class that is used can be customized using the config.plugin_loader option. Those monkey patching the plugin loading subsystem take note, the internals changing here will likely break your modifications. The good news is that it should be substantially easier to hook into the plugin locating and loading process now. [Marcel Molina Jr.] + +* Added assumption that all plugin creators desire to be sharing individuals and release their work under the MIT license [David Heinemeier Hansson] + +* Added source-annotations extractor tasks to rake [Jamis Buck]. This allows you to add FIXME, OPTIMIZE, and TODO comments to your source code that can then be extracted in concert with rake notes (shows all), rake notes:fixme, rake notes:optimize and rake notes:todo. + +* Added fixtures :all to test_helper.rb to assume that most people just want all their fixtures loaded all the time [David Heinemeier Hansson] + +* Added config/initializers where all ruby files within it are automatically loaded after the Rails configuration is done, so you don't have to litter the environment.rb file with a ton of mixed stuff [David Heinemeier Hansson] + +* For new apps, generate a random secret for the cookie-based session store. [Jeremy Kemper] + +* Stop swallowing errors during rake test [Michael Koziarski] + +* Update Rails Initializer to use ActionController::Base#view_paths [Rick Olson] + +* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler] + +* Plugins may be symlinked in vendor/plugins. #4245 [brandon, progrium@gmail.com] + +* Resource generator depends on the model generator rather than duplicating it. #7269 [bscofield] + +* Add/Update usage documentation for script/destroy, resource generator and scaffold_resource generator. Closes #7092, #7271, #7267. [bscofield] + +* Update to script.aculo.us 1.7.0. [Thomas Fuchs] + +* Update to Prototype 1.5.0. [Sam Stephenson] + +* Generator: use destination path for diff tempfiles. #7015 [alfeld] + +* Fixed that webrick would strip leading newlines and hang connection #4156 [psross] + +* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick Olson] + If you really want your plugins to reload, add this to the very top of init.rb: + + Dependencies.load_once_paths.delete(lib_path) + +* Allow config.to_prepare to work, make the dispatcher safe to 're require'. [Michael Koziarski, Nicholas Seckar] + +* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 [fearoffish] + +* Fix Webrick Daemon dispatching bug regarding a bad current working directory. Closes #4899 [Rick Olson] + +* Make config.plugins affect the load path and the dependencies system. Allows you to control plugin loading order, and keep disabled plugins off the load path. [James Adam] + +* Don't generate a components directory in new Rails apps. [Jeremy Kemper] + +* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [David Heinemeier Hansson] + +* Added one-letter aliases for the three default environments to script/console, so script/console p will load the production environment (t for test, d for development) [David Heinemeier Hansson] + +* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [David Heinemeier Hansson] + +* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar] + +* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [David Heinemeier Hansson] + +* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [David Heinemeier Hansson] + +* Update to Prototype and script.aculo.us [5579]. [Thomas Fuchs] + +* Made script/server work with -e and -d when using Mongrel [David Heinemeier Hansson] + +* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again [Thomas Fuchs] + +* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Lütke] + +* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 [fearoffish] + +* Update to Prototype 1.5.0_rc2. [Sam Stephenson] + +* Add grep-based fallback to reaper, to work in pidless setups [Jamis Buck] + +* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished. [Jeremy Kemper] + +* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. [Nicholas Seckar] + +* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT. #6584 [jchris] + +* Remove temporary crutch to help ApplicationController be unloaded. Closes #6496. [Nicholas Seckar] + +* scaffold_resource generator uses _path named routes and head instead of render :nothing => true. #6545 [Josh Susser] + +* Generator can show diff on file collision to help you decide whether to skip or overwrite. #6364 [jeffw, Jeremy Kemper] + +* Generated directories are recursively svn added, like mkdir -p. #6416 [NeilW] + +* resource and scaffold_resource generators add a restful route to config/routes.rb [Jeremy Kemper] + +* Revert environment changes for autoload_paths. [Michael Koziarski] + +* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs] + +* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Michael Koziarski] + +* Added option to script/process/spawner of specifying the binding address #5133 [Dee Zsombor] + +* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio] + +* Update scaffold to use new form_tag block functionality. Closes #6480. [Bob Silva] + +* Plugin generator: check for class collisions. #4833 [vinbarnes@gmail.com] + +* Mailer generator: handle mailers in modules, set mime_version in unit test. [Jeremy Kemper] + +* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Michael Koziarski] + +* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example: + + config.plugins = %w[ routing_navigator simply_helpful ] + +* Clean up html on included error pages. [Tim Lucas] + +* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [David Heinemeier Hansson] + +* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs] + +* PostgreSQL: db:test:purge closes open database connections first. #6236 [alex] + +* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul@paulbutcher.com] + +* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [Bob Silva] + +* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon@opensoul.org] + +* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [David Heinemeier Hansson] + +* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples: + + ./script/generate model post title:string created_on:date body:text published:boolean + +* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [David Heinemeier Hansson] + +* script/runner can run files, pass on arguments, and be used as a shebang. #6286 [Tuxie, dlpond] + #!/usr/bin/env /path/to/my/app/script/runner + # Example: just start using your models as if you are in script/console + Product.find(:all).each { |product| product.check_inventory } + +* Look for rake tasks in plugin subdirs. #6259 [obrie] + +* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [David Heinemeier Hansson] + +* Updated prototype.js to 1.5.0_rc1 with latest fixes. [Rick Olson] + + - XPATH support + - Make Form.getElements() return elements in the correct order + - fix broken Form.serialize return + +* session_migration generator adds an index on updated_at. #6207 [grg] + +* script/server creates the tmp/pids directory. #6204 [jonathan] + +* Fix script/console --sandbox for internal transactions changes. #5738 [Chris McGrath, charles.gerungan@gmail.com] + +* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As +documented in initializer.rb, the workaround is: + + config.autoload_paths += Dir[RAILS_ROOT + '/app/models/*/'] + +References #6031. [Nicholas Seckar] + +* Update to script.aculo.us 1.6.3 [Thomas Fuchs] + +* Update to Prototype 1.5.0_rc1 [sam] + +* Formally Deprecate the old rake tasks. [Michael Koziarski] + +* Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark] + +* Remove Dir.chdir in the Webrick DispatchServlet#initialize method. Fix bad path errors when trying to load config/routes.rb. [Rick Olson] + +* Tighten rescue clauses. #5985 [james@grayproductions.net] + +* Cleaning up tests. [Kevin Clark, Jeremy Kemper] + +* Add Dependencies.load_once_paths. [Nicholas Seckar] + +* Updated to script.aculo.us 1.6.2 [Thomas Fuchs] + +* Assign Routing.controller_paths; fix script/about and rails info controller. [Nicholas Seckar] + +* Don't warn dispatcher of Reloadable deprecations. [Nicholas Seckar] + +* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base [Rick Olson] + + ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed. + ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback. + +* Add missing mock directories from the autoload_paths configuration. [Rick Olson] + +* Nested controller scaffolding also nests the generated layout. [iain d broadfoot] + +* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. [Nicholas Seckar] + +* Add autoload_paths support to Initializer. [Nicholas Seckar] + +* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. [Rick Olson] + +* Clarify usage of script/plugin source. Closes #5344. [James Adam] + +* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar] + +* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279. [Rick Olson] + +* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats@imediatec.co.uk] + +* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [Shugo Maeda] + +* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. [Rick Olson] + +* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. [Jamis Buck] + +* Fix script/plugin about so it uses about.yml and not meta.yml. [James Adam] + +* Dispatcher processes rescued actions with the same controller that processed the request. #4625 [sd@notso.net] + +* rails -d frontbase to create a new project with a frontbase database.yml. #4945 [mlaster@metavillage.com] + +* Ensure the logger is initialized. #5629 [mike@clarkware.com] + +* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples: + + spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available + spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI + spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel + spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available + spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds + + Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster. + +* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 [evan@protest.net] + +* db:test:clone should remove existing tables before reloading the schema. #5607 [sveit@tradeharbor.com] + +* Fixed migration generation for class names like ACLController #5197 [brad@madriska.com] + +* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi@drecom.co.jp]. Examples: + + irb(#):002:0> show_source_list + 0001 class TopController < ApplicationController + 0002 def show + 0003-> breakpoint + 0004 end + 0005 + 0006 def index + 0007 end + 0008 + => "/path/to/rails/root/app/controllers/top_controller.rb" + + irb(#):004:0> show_call_stack 3 + vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint' + vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint' + app/controllers/top_controller.rb:3:in `show' + => "/path/to/rails/root/app/controllers/top_controller.rb:3" + +* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 [nils@alumni.rice.edu] + +* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 [mike@clarkware.com] + +* Don't assume Active Record is available. #5497 [bob@sporkmonger.com] + +* Mongrel: script/server works on Win32. #5499 [jeremydurham@gmail.com] + +* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [Nick Sieger] + +* Mongrel support for script/server. #5475 [jeremydurham@gmail.com] + +* Fix script/plugin so it doesn't barf on invalid URLs [Rick Olson] + +* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA] + +* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick Olson] + +* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi@drecom.co.jp] + +* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 [elliot@townx.org] + +* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. [Jamis Buck] + +* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson] + +* Added lib/ to the directories that will get application docs generated [David Heinemeier Hansson] + +* Add observer generator. Closes #5167. [François Beausoleil] + +* Session migration generator obeys pluralize_table_names. #5145 [James Adam] + +* rake test:recent understands subdirectories. #2925 [jerrett@bravenet.com] + +* The app generator detects the XAMPP package's MySQL socket location. #3832 [elliot@townx.org] + +* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug@owl.me.uk] + +* Distinguish the spawners for different processes [David Heinemeier Hansson] + +* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [David Heinemeier Hansson] + +* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov] + +* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.] + +* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [David Heinemeier Hansson] + +* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [David Heinemeier Hansson] + + +*1.2.3* (March 12th, 2007) + +* Ruby 1.8.6 compatibility + +* Windows: include MinGW in RUBY_PLATFORM check. #2982 [okkez000@gmail.com, Kaspar Schiess] + +* Stop swallowing errors during rake test [Michael Koziarski] + + +*1.2.2* (February 5th, 2007) + +* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler] + +* Require the dispatcher for Rails::Configuration#to_prepare. [Rick Olson] + + +*1.2.1* (January 16th, 2007) + +* Updated to Active Record 1.15.1, Action Pack 1.13.1, Action Mailer 1.3.1, Action Web Service 1.2.1 + + +*1.2.0* (January 16th, 2007) + +* Update to Prototype 1.5.0. [Sam Stephenson] + +* Generator: use destination path for diff tempfiles. #7015 [alfeld] + +* Fixed that webrick would strip leading newlines and hang connection #4156 [psross] + +* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick Olson] + If you really want your plugins to reload, add this to the very top of init.rb: + + Dependencies.load_once_paths.delete(lib_path) + +* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 [fearoffish] + +* Fix Webrick Daemon dispatching bug regarding a bad current working directory. Closes #4899 [Rick Olson] + +* Make config.plugins affect the load path and the dependencies system. Allows you to control plugin loading order, and keep disabled plugins off the load path. [James Adam] + +* Don't generate a components directory in new Rails apps. [Jeremy Kemper] + +* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [David Heinemeier Hansson] + +* Deprecated the name route "root" as it'll be used as a shortcut for map.connect '' in Rails 2.0 [David Heinemeier Hansson] + +* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [David Heinemeier Hansson] + +* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar] + +* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [David Heinemeier Hansson] + +* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [David Heinemeier Hansson] + +* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs] + +* Made script/server work with -e and -d when using Mongrel [David Heinemeier Hansson] + +* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Lütke] + +* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 [fearoffish] + +* Add grep-based fallback to reaper, to work in pidless setups [Jamis Buck] + +* Only wrap request processing with our USR1 signal handler so FastCGI can trap it and raise an exception while waiting for connections. Idle processes exit immediately rather than waiting for another request; active processes gracefully exit when the request is finished. [Jeremy Kemper] + +* Alter prior change to use require_dependency instead of require_or_load. Causes ApplicationController to be reloaded again. Closes #6587. [Nicholas Seckar] + +* Rake: use absolute paths to load lib and vendor tasks so they may be run outside of RAILS_ROOT. #6584 [jchris] + +* scaffold_resource generator uses _path named routes and head instead of render :nothing => true. #6545 [Josh Susser] + +* Generator can show diff on file collision to help you decide whether to skip or overwrite. #6364 [jeffw, Jeremy Kemper] + +* Generated directories are recursively svn added, like mkdir -p. #6416 [NeilW] + +* resource and scaffold_resource generators add a restful route to config/routes.rb [Jeremy Kemper] + +* Revert environment changes for autoload_paths. [Michael Koziarski] + +* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Michael Koziarski] + +* Added option to script/process/spawner of specifying the binding address #5133 [Dee Zsombor] + +* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio] + +* Update scaffold to use new form_tag block functionality. Closes #6480. [Bob Silva] + +* Plugin generator: check for class collisions. #4833 [vinbarnes@gmail.com] + +* Mailer generator: handle mailers in modules, set mime_version in unit test. [Jeremy Kemper] + +* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Michael Koziarski] + +* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example: + + config.plugins = %w[ routing_navigator simply_helpful ] + +* Clean up html on included error pages. [Tim Lucas] + +* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [David Heinemeier Hansson] + +* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs] + +* PostgreSQL: db:test:purge closes open database connections first. #6236 [alex] + +* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul@paulbutcher.com] + +* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [Bob Silva] + +* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon@opensoul.org] + +* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [David Heinemeier Hansson] + +* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples: + + ./script/generate model post title:string created_on:date body:text published:boolean + +* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [David Heinemeier Hansson] + +* script/runner can run files, pass on arguments, and be used as a shebang. #6286 [Tuxie, dlpond] + #!/usr/bin/env /path/to/my/app/script/runner + # Example: just start using your models as if you are in script/console + Product.find(:all).each { |product| product.check_inventory } + +* Look for rake tasks in plugin subdirs. #6259 [obrie] + +* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [David Heinemeier Hansson] + +* session_migration generator adds an index on updated_at. #6207 [grg] + +* script/server creates the tmp/pids directory. #6204 [jonathan] + +* Fix script/console --sandbox for internal transactions changes. #5738 [Chris McGrath, charles.gerungan@gmail.com] + +* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As +documented in initializer.rb, the workaround is: + + config.autoload_paths += Dir[RAILS_ROOT + '/app/models/*/'] + +References #6031. [Nicholas Seckar] + +* Update to script.aculo.us 1.6.3 [Thomas Fuchs] + +* Formally Deprecate the old rake tasks. [Michael Koziarski] + +* Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark] + +* Remove Dir.chdir in the Webrick DispatchServlet#initialize method. Fix bad path errors when trying to load config/routes.rb. [Rick Olson] + +* Tighten rescue clauses. #5985 [james@grayproductions.net] + +* Cleaning up tests. [Kevin Clark, Jeremy Kemper] + +* Add Dependencies.load_once_paths. [Nicholas Seckar] + +* Assign Routing.controller_paths; fix script/about and rails info controller. [Nicholas Seckar] + +* Don't warn dispatcher of Reloadable deprecations. [Nicholas Seckar] + +* Rearrange application resetting and preparation, fix bug with leaking subclasses hash in ActiveRecord::Base [Rick Olson] + + ActiveRecord::Base.reset_subclasses is called before Dependencies are cleared and classes removed. + ActiveRecord::Base.instantiate_observers is called during a Dispatcher preparation callback. + +* Add missing mock directories from the autoload_paths configuration. [Rick Olson] + +* Nested controller scaffolding also nests the generated layout. [iain d broadfoot] + +* Add "require 'dispatcher'" to webrick server in the continuing quest to squash webrick weirdness. [Nicholas Seckar] + +* Add autoload_paths support to Initializer. [Nicholas Seckar] + +* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. [Rick Olson] + +* Clarify usage of script/plugin source. Closes #5344. [James Adam] + +* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar] + +* Tweak the Rails load order so observers are loaded after plugins, and reloaded in development mode. Closed #5279. [Rick Olson] + +* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats@imediatec.co.uk] + +* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [Shugo Maeda] + +* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. [Rick Olson] + +* Make sure script/reaper only reaps dispatcher pids by default, and not the spawner's pid. [Jamis Buck] + +* Fix script/plugin about so it uses about.yml and not meta.yml. [James Adam] + +* Dispatcher processes rescued actions with the same controller that processed the request. #4625 [sd@notso.net] + +* rails -d frontbase to create a new project with a frontbase database.yml. #4945 [mlaster@metavillage.com] + +* Ensure the logger is initialized. #5629 [mike@clarkware.com] + +* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples: + + spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available + spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI + spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, 8003, and 8004 using Mongrel + spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109 using Mongrel if available + spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to 9102 and attempts start them every 5 seconds + + Also note that script/process/reaper is Mongrel capable. So the combination of spawner and reaper is a built-in alternative to something like mongrel_cluster. + +* Update scaffolding functional tests to use :id => people(:first) instead of :id => 1. #5612 [evan@protest.net] + +* db:test:clone should remove existing tables before reloading the schema. #5607 [sveit@tradeharbor.com] + +* Fixed migration generation for class names like ACLController #5197 [brad@madriska.com] + +* Added show_source_list and show_call_stack to breakpoints to make it easier to get context #5476 [takiuchi@drecom.co.jp]. Examples: + + irb(#):002:0> show_source_list + 0001 class TopController < ApplicationController + 0002 def show + 0003-> breakpoint + 0004 end + 0005 + 0006 def index + 0007 end + 0008 + => "/path/to/rails/root/app/controllers/top_controller.rb" + + irb(#):004:0> show_call_stack 3 + vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint' + vendor/rails/railties/lib/breakpoint.rb:536:in `breakpoint' + app/controllers/top_controller.rb:3:in `show' + => "/path/to/rails/root/app/controllers/top_controller.rb:3" + +* Generate scaffold layout in subdirectory appropriate to its module nesting. #5511 [nils@alumni.rice.edu] + +* Mongrel: script/server tails the rails log like it does with lighttpd. Prefer mongrel over lighttpd. #5541 [mike@clarkware.com] + +* Don't assume Active Record is available. #5497 [bob@sporkmonger.com] + +* Mongrel: script/server works on Win32. #5499 [jeremydurham@gmail.com] + +* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [Nick Sieger] + +* Mongrel support for script/server. #5475 [jeremydurham@gmail.com] + +* Fix script/plugin so it doesn't barf on invalid URLs [Rick Olson] + +* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA] + +* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick Olson] + +* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi@drecom.co.jp] + +* Create temporary dirs relative to RAILS_ROOT when running script/server #5014 [elliot@townx.org] + +* Minor tweak to dispatcher to use recognize instead of recognize!, as per the new routes. [Jamis Buck] + +* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson] + +* Added lib/ to the directories that will get application docs generated [David Heinemeier Hansson] + +* Add observer generator. Closes #5167. [François Beausoleil] + +* Session migration generator obeys pluralize_table_names. #5145 [James Adam] + +* rake test:recent understands subdirectories. #2925 [jerrett@bravenet.com] + +* The app generator detects the XAMPP package's MySQL socket location. #3832 [elliot@townx.org] + +* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug@owl.me.uk] + +* Distinguish the spawners for different processes [David Heinemeier Hansson] + +* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [David Heinemeier Hansson] + +* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov] + +* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.] + +* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [David Heinemeier Hansson] + +* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [David Heinemeier Hansson] + + +*1.1.6* (August 10th, 2006) + +* Additional security patch + + +*1.1.5* (August 8th, 2006) + +* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. #4857 [Alisdair McDiarmid] + +* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.] + +* Includes critical security patch + + +*1.1.4* (June 29th, 2006) + +* Remove use of opts.on { |options[:name] } style hash assignment. References #4440. [headius@headius.com] + +* Updated to Action Pack 1.12.3, ActionWebService 1.1.4, ActionMailer 1.2.3 + + +*1.1.3* (June 27th, 2006) + +* Updated to Active Record 1.14.3, Action Pack 1.12.2, ActionWebService 1.1.3, ActionMailer 1.2.2 + + +*1.1.2* (April 9th, 2006) + +* Mention in docs that config.frameworks doesn't work when getting Rails via Gems. Closes #4857. [Alisdair McDiarmid] + +* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.] + +* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [David Heinemeier Hansson] + +* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [David Heinemeier Hansson] + + +*1.1.1* (April 6th, 2006) + +* Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) [ruben.nine@gmail.com] + +* Update to Prototype 1.5.0_rc0 [Sam Stephenson] + +* Fixed that the -r/--ruby path option of the rails command was not being respected #4549 [ryan.raaum@gmail.com] + +* Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html [David Heinemeier Hansson] + +* Fixed that rake clone_structure_to_test should quit on pgsql if the dump is unsuccesful #4585 [augustz@augustz.com] + +* Fixed that rails --version should have the return code of 0 (success) #4560 [blair@orcaware.com] + +* Install alias so Rails::InfoController is accessible at /rails_info. Closes #4546. [Nicholas Seckar] + +* Fixed that spawner should daemonize if running in repeat mode [David Heinemeier Hansson] + +* Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release [David Heinemeier Hansson] + +* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs] + +* Use --simple-prompt instead of --prompt-mode simple for console compatibility with Windows/Ruby 1.8.2 #4532 [starr@starrnhorne.com] + +* Make Rails::VERSION implicitly loadable #4491. [Nicholas Seckar] + +* Fixed rake rails:freeze:gems #4518 [benji@silverinsanity.com] + +* Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with [David Heinemeier Hansson] + +* Added gem binding of apps generated through the rails command to the gems of they were generated with [Nicholas Seckar] + +* Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf [David Heinemeier Hansson] + +* Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf [David Heinemeier Hansson] + +* Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar] + + +*1.1.0* (March 27th, 2006) + +* Allow db:fixtures:load to load a subset of the applications fixtures. [Chad Fowler] + + ex. + + rake db:fixtures:load FIXTURES=customers,plans + +* Update to Prototype 1.5.0_pre1 [Sam Stephenson] + +* Update to script.aculo.us 1.6 [Thomas Fuchs] + +* Add an integration_test generator [Jamis Buck] + +* Make all ActionView helpers available in the console from the helper method for debugging purposes. n.b.: Only an 80% solution. Some stuff won't work, most will. [Marcel Molina Jr.] + + ex. + + >> puts helper.options_for_select([%w(a 1), %w(b 2), %w(c 3)]) + + + + => nil + +* Replaced old session rake tasks with db:sessions:create to generate a migration, and db:sessions:clear to remove sessions. [Rick Olson] + +* Reject Ruby 1.8.3 when loading Rails; extract version checking code. [Chad Fowler] + +* Remove explicit loading of RailsInfo and RailsInfoController. [Nicholas Seckar] + +* Move RailsInfo and RailsInfoController to Rails::Info and Rails::InfoController. [Nicholas Seckar] + +* Extend load path with Railties' builtin directory to make adding support code easy. [Nicholas Seckar] + +* Fix the rails_info controller by explicitly loading it, and marking it as not reloadable. [Nicholas Seckar] + +* Fixed rails:freeze:gems for Windows #3274 [paul@paulbutcher.com] + +* Added 'port open?' check to the spawner when running in repeat mode so we don't needlessly boot the dispatcher if the port is already in use anyway #4089 [guy.naor@famundo.com] + +* Add verification to generated scaffolds, don't allow get for unsafe actions [Michael Koziarski] + +* Don't replace application.js in public/javascripts if it already exists [Cody Fauser] + +* Change test:uncommitted to delay execution of `svn status` by using internal Rake API's. [Nicholas Seckar] + +* Use require_library_or_gem to load rake in commands/server.rb. Closes #4205. [rob.rasmussen@gmail.com] + +* Use the Rake API instead of shelling out to create the tmp directory in commands/server.rb. [Chad Fowler] + +* Added a backtrace to the evil WSOD (White Screen of Death). Closes #4073. TODO: Clearer exceptions [Rick Olson] + +* Added tracking of database and framework versions in script/about #4088 [charles.gerungan@gmail.com/Rick Olson] + +* Added public/javascripts/application.js as a sample since it'll automatically be included in javascript_include_tag :defaults [David Heinemeier Hansson] + +* Added socket cleanup for lighttpd, both before and after [David Heinemeier Hansson] + +* Added automatic creation of tmp/ when running script/server [David Heinemeier Hansson] + +* Added silence_stream that'll work on both STDERR or STDOUT or any other stream and deprecated silence_stderr in the process [David Heinemeier Hansson] + +* Added reload! method to script/console to reload all models and others that include Reloadable without quitting the console #4056 [esad@esse.at] + +* Added that rake rails:freeze:edge will now just export all the contents of the frameworks instead of just lib, so stuff like rails:update:scripts, rails:update:javascripts, and script/server on lighttpd still just works #4047 [David Heinemeier Hansson] + +* Added fix for upload problems with lighttpd from Safari/IE to config/lighttpd.conf #3999 [Thijs van der Vossen] + +* Added test:uncommitted to test changes since last checkin to Subversion #4035 [technomancy@gmail.com] + +* Help script/about print the correct svn revision when in a non-English locale. #4026 [babie7a0@ybb.ne.jp] + +* Add 'app' accessor to script/console as an instance of Integration::Session [Jamis Buck] + +* Generator::Base#usage takes an optional message argument which defaults to Generator::Base#usage_message. [Jeremy Kemper] + +* Remove the extraneous AR::Base.threaded_connections setting from the webrick server. [Jeremy Kemper] + +* Add integration test support to app generation and testing [Jamis Buck] + +* Added namespaces to all tasks, so for example load_fixtures is now db:fixtures:load. All the old task names are still valid, they just point to the new namespaced names. "rake -T" will only show the namespaced ones, though [David Heinemeier Hansson] + +* CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by default instead of :sql. This means that we'll assume you want to live in the world of db/schema.rb where the grass is green and the girls are pretty. If your schema contains un-dumpable elements, such as constraints or database-specific column types, you just got an invitation to either 1) patch the dumper to include foreign key support, 2) stop being db specific, or 3) just change the default in config/environment.rb to config.active_record.schema_format = :sql -- we even include an example for that on new Rails skeletons now. Brought to you by the federation of opinionated framework builders! [David Heinemeier Hansson] + +* Added -r/--repeat option to script/process/spawner that offers the same loop protection as the spinner did. This deprecates the script/process/spinner, so it's no longer included in the default Rails skeleton, but still available for backwards compatibility #3461 [ror@andreas-s.net] + +* Added collision option to template generation in generators #3329 [anna@wota.jp]. Examples: + + m.template "stuff.config" , "config/stuff.config" , :collision => :skip + m.template "auto-stamping", "config/generator.log", :collision => :force + +* Added more information to script/plugin's doings to ease debugging #3755 [Rick Olson] + +* Changed the default configuration for lighttpd to use tmp/sockets instead of log/ for the FastCGI sockets [David Heinemeier Hansson] + +* Added a default configuration of the FileStore for fragment caching if tmp/cache is available, which makes action/fragment caching ready to use out of the box with no additional configuration [David Heinemeier Hansson] + +* Changed the default session configuration to place sessions in tmp/sessions, if that directory is available, instead of /tmp (this essentially means a goodbye to 9/10 White Screen of Death errors and should have web hosting firms around the world cheering) [David Heinemeier Hansson] + +* Added tmp/sessions, tmp/cache, and tmp/sockets as default directories in the Rails skeleton [David Heinemeier Hansson] + +* Added that script/generate model will now automatically create a migration file for the model created. This can be turned off by calling the generator with --skip-migration [David Heinemeier Hansson] + +* Added -d/--database option to the rails command, so you can do "rails --database=sqlite2 myapp" to start a new application preconfigured to use SQLite2 as the database. Removed the configuration examples from SQLite and PostgreSQL from the default MySQL configuration [David Heinemeier Hansson] + +* Allow script/server -c /path/to/lighttpd.conf [Jeremy Kemper] + +* Remove hardcoded path to reaper script in script/server [Jeremy Kemper] + +* Update script.aculo.us to V1.5.3 [Thomas Fuchs] + +* Added SIGTRAP signal handler to RailsFCGIHandler that'll force the process into a breakpoint after the next request. This breakpoint can then be caught with script/breakpointer and give you access to the Ruby image inside that process. Useful for debugging memory leaks among other things [David Heinemeier Hansson] + +* Changed default lighttpd.conf to use CWD from lighttpd 1.4.10 that allows the same configuration to be used for both detach and not. Also ensured that auto-repeaping of FCGIs only happens when lighttpd is not detached. [David Heinemeier Hansson] + +* Added Configuration#after_initialize for registering a block which gets called after the framework is fully initialized. Useful for things like per-environment configuration of plugins. [Michael Koziarski] + +* Added check for RAILS_FRAMEWORK_ROOT constant that allows the Rails framework to be found in a different place than vendor/rails. Should be set in boot.rb. [David Heinemeier Hansson] + +* Fixed that static requests could unlock the mutex guarding dynamic requests in the WEBrick servlet #3433 [tom@craz8.com] + +* Fixed documentation tasks to work with Rake 0.7.0 #3563 [kazuhiko@fdiary.net] + +* Update to Prototype 1.5.0_pre0 [Sam Stephenson] + +* Sort the list of plugins so we load in a consistent order [Rick Olson] + +* Show usage when script/plugin is called without arguments [tom@craz8.com] + +* Corrected problems with plugin loader where plugins set 'name' incorrectly #3297 [anna@wota.jp] + +* Make migration generator only report on exact duplicate names, not partial dupliate names. #3442 [jeremy@planetargon.com Marcel Molina Jr.] + +* Fix typo in mailer generator USAGE. #3458 [chriztian.steinmeier@gmail.com] + +* Ignore version mismatch between pg_dump and the database server. #3457 [Simon Stapleton] + +* Reap FCGI processes after lighttpd exits. [Sam Stephenson] + +* Honor ActiveRecord::Base.pluralize_table_names when creating and destroying session store table. #3204. [rails@bencurtis.com, Marcel Molina Jr.] + +*1.0.0* (December 13th, 2005) + +* Update instructions on how to find and install generators. #3172. [Chad Fowler] + +* Generator looks in vendor/generators also. [Chad Fowler] + +* Generator copies files in binary mode. #3156 [minimudboy@gmail.com] + +* Add builtin/ to the gemspec. Closes #3047. [Nicholas Seckar, Sam Stephenson] + +* Add install.rb file to plugin generation which is loaded, if it exists, when you install a plugin. [Marcel Molina Jr.] + +* Run initialize_logger in script/lighttpd to ensure the log file exists before tailing it. [Sam Stephenson] + +* Make load_fixtures include csv fixtures. #3053. [me@mdaines.com] + +* Fix freeze_gems so that the latest rails version is dumped by default. [Nicholas Seckar] + +* script/plugin: handle root paths and plugin names which contain spaces. #2995 [justin@aspect.net] + +* Model generator: correct relative path to test_helper in unit test. [Jeremy Kemper] + +* Make the db_schema_dump task honor the SCHEMA environment variable if present the way db_schema_import does. #2931. [Blair Zajac] + +* Have the lighttpd server script report the actual ip to which the server is bound. #2903. [Adam] + +* Add plugin library directories to the load path after the lib directory so that libraries in the lib directory get precedence. #2910. [James Adam] + +* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. [anonymous] + +* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 [Dreamer3] + +* Introducing the session_migration generator. Creates an add_session_table migration. Allows generator to specify migrations directory. #2958, #2960 [Rick Olson] + +* script/console uses RAILS_ENV environment variable if present. #2932 [Blair Zajac + +* Windows: eliminate the socket option in database.yml. #2924 [Wayne Vucenic ] + +* Eliminate nil from newly generated logfiles. #2927 [Blair Zajac ] + +* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.] + +* Eliminate Subversion dependencies in scripts/plugin. Correct install options. Introduce --force option to reinstall a plugin. Remove useless --long option for list. Use --quiet to quiet the download output and --revision to update to a specific svn revision. #2842 [Chad Fowler, Rick Olson] + +* SQLite: the clone_structure_to_test and purge_test_database Rake tasks should always use the test environment. #2846 [Rick Olson] + +* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko@fdiary.net] + +* Pass __FILE__ when evaluating plugins' init.rb. #2817 [James Adam] + +* Better svn status matching for generators. #2814 [François Beausoleil , Blair Zajac ] + +* Don't reload routes until plugins have been loaded so they have a chance to extend the routing capabilities [David Heinemeier Hansson] + +* Don't detach or fork for script/server tailing [Nicholas Seckar] + +* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path [David Heinemeier Hansson] + +* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [David Heinemeier Hansson] #2660 + +* Correct versioning in :freeze_gems Rake task. #2778 [Jakob Skjerning, Jeremy Kemper] + +* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. [Sam Stephenson] + + +*0.14.3 (RC4)* (November 7th, 2005) + +* Add 'add_new_scripts' rake task for adding new rails scripts to script/* [Jamis Buck] + +* Remove bogus hyphen from script/process/reaper calls to 'ps'. #2767 [anonymous] + +* Copy lighttpd.conf when it is first needed, instead of on app creation [Jamis Buck] + +* Use require_library_or_gem 'fcgi' in script/server [Sam Stephenson] + +* Added default lighttpd config in config/lighttpd.conf and added a default runner for lighttpd in script/server (works like script/server, but using lighttpd and FastCGI). It will use lighttpd if available, otherwise WEBrick. You can force either or using 'script/server lighttpd' or 'script/server webrick' [David Heinemeier Hansson] + +* New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins']. [Jeremy Kemper] + +* Plugins are discovered in nested paths, so you can organize your plugins directory as you like. [Jeremy Kemper] + +* Refactor load_plugin from load_plugins. #2757 [alex.r.moon@gmail.com] + +* Make use of silence_stderr in script/lighttpd, script/plugin, and Rails::Info [Sam Stephenson] + +* Enable HTTP installation of plugins when svn isn't avaialable. Closes #2661. [Chad Fowler] + +* Added script/about to display formatted Rails::Info output [Sam Stephenson] + +* Added Rails::Info to catalog assorted information about a Rails application's environment [Sam Stephenson] + +* Tail the logfile when running script/server lighttpd in the foreground [Sam Stephenson] + +* Try to guess the port number from config/lighttpd.conf in script/server lighttpd [Sam Stephenson] + +* Don't reap spawn-fcgi. #2727 [matthew@walker.wattle.id.au] + +* Reaper knows how to find processes even if the dispatch path is very long. #2711 [matthew@walker.wattle.id.au] + +* Make fcgi handler respond to TERM signals with an explicit exit [Jamis Buck] + +* Added demonstration of fixture use to the test case generated by the model generator [David Heinemeier Hansson] + +* If specified, pass PostgreSQL client character encoding to createdb. #2703 [Kazuhiko ] + +* Catch CGI multipart parse errors. Wrap dispatcher internals in a failsafe response handler. [Jeremy Kemper] + +* The freeze_gems Rake task accepts the VERSION environment variable to decide which version of Rails to pull into vendor/rails. [Chad Fowler, Jeremy Kemper] + +* Removed script.aculo.us.js, builder.js and slider.js (preperation for move of scriptaculous extensions to plugins, core scriptaculous will remain in Railties) [Thomas Fuchs] + +* The freeze_edge Rake task does smarter svn detection and can export a specific revision by passing the REVISION environment variable. For example: rake freeze_edge REVISION=1234. #2663 [Rick Olson] + +* Comment database.yml and include PostgreSQL and SQLite examples. [Jeremy Kemper] + +* Improve script/plugin on Windows. #2646 [Chad Fowler] + +* The *_plugindoc Rake tasks look deeper into the plugins' lib directories. #2652 [bellis@deepthought.org] + +* The PostgreSQL :db_structure_dump Rake task limits its dump to the schema search path in database.yml. [Anatol Pomozov ] + +* Add task to generate rdoc for all installed plugins. [Marcel Molina Jr.] + +* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs] + +* Add default Mac + DarwinPorts MySQL socket locations to the app generator. [Jeremy Kemper] + +* Migrations may be destroyed: script/destroy migration foo. #2635 [Charles M. Gerungan , Jamis Buck, Jeremy Kemper] + +* Added that plugins can carry generators and that generator stub files can be created along with new plugins using script/generate plugin --with-generator [David Heinemeier Hansson] + +* Removed app/apis as a default empty dir since its automatically created when using script/generate web_service [David Heinemeier Hansson] + +* Added script/plugin to manage plugins (install, remove, list, etc) [Ryan Tomayko] + +* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) [David Heinemeier Hansson] + +* Added plugin generator to create a stub structure for a new plugin in vendor/plugins (see "script/generate plugin" for help) [David Heinemeier Hansson] + +* Fixed scaffold generator when started with only 1 parameter #2609 [self@mattmower.com] + +* rake should run functional tests even if the unit tests have failures [Jim Weirich] + +* Back off cleanpath to be symlink friendly. Closes #2533 [Nicholas Seckar] + +* Load rake task files in alphabetical order so you can build dependencies and count on them #2554 [Blair Zajac] + + +*0.14.2 (RC3)* (October 26th, 2005) + +* Constants set in the development/test/production environment file are set in Object + +* Scaffold generator pays attention to the controller name. #2562 [self@mattmower.com] + +* Include tasks from vendor/plugins/*/tasks in the Rakefile #2545 [Rick Olson] + + +*0.14.1 (RC2)* (October 19th, 2005) + +* Don't clean RAILS_ROOT on windows + +* Remove trailing '/' from RAILS_ROOT [Nicholas Seckar] + +* Upgraded to Active Record 1.12.1 and Action Pack 1.10.1 + + +*0.14.0 (RC1)* (October 16th, 2005) + +* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators [Tobias Lütke] + +* Fix rake dev and related commands [Nicholas Seckar] + +* The rails command tries to deduce your MySQL socket by running `mysql_config +--socket`. If it fails, default to /path/to/your/mysql.sock + +* Made the rails command use the application name for database names in the tailored database.yml file. Example: "rails ~/projects/blog" will use "blog_development" instead of "rails_development". [Florian Weber] + +* Added Rails framework freezing tasks: freeze_gems (freeze to current gems), freeze_edge (freeze to Rails SVN trunk), unfreeze_rails (float with newest gems on system) + +* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. [Tobias Lütke] + +* Added cleaning of RAILS_ROOT to useless elements such as '../non-dot-dot/'. Provides cleaner backtraces and error messages. [Nicholas Seckar] + +* Made the instantiated/transactional fixtures settings be controlled through Rails::Initializer. Transactional and non-instantiated fixtures are default from now on. [Florian Weber] + +* Support using different database adapters for development and test with ActiveRecord::Base.schema_format = :ruby [Sam Stephenson] + +* Make webrick work with session(:off) + +* Add --version, -v option to the Rails command. Closes #1840. [stancell] + +* Update Prototype to V1.4.0_pre11, script.aculo.us to V1.5_rc3 [2504] and fix the rails generator to include the new .js files [Thomas Fuchs] + +* Make the generator skip a file if it already exists and is identical to the new file. + +* Add experimental plugin support #2335 + +* Made Rakefile aware of new .js files in script.aculo.us [Thomas Fuchs] + +* Make table_name and controller_name in generators honor AR::Base.pluralize_table_names. #1216 #2213 [kazuhiko@fdiary.net] + +* Clearly label functional and unit tests in rake stats output. #2297 [lasse.koskela@gmail.com] + +* Make the migration generator only check files ending in *.rb when calculating the next file name #2317 [Chad Fowler] + +* Added prevention of duplicate migrations from the generator #2240 [François Beausoleil] + +* Add db_schema_dump and db_schema_import rake tasks to work with the new ActiveRecord::SchemaDumper (for dumping a schema to and reading a schema from a ruby file). + +* Reformed all the config/environments/* files to conform to the new Rails::Configuration approach. Fully backwards compatible. + +* Added create_sessions_table, drop_sessions_table, and purge_sessions_table as rake tasks for databases that supports migrations (MySQL, PostgreSQL, SQLite) to get a table for use with CGI::Session::ActiveRecordStore + +* Added dump of schema version to the db_structure_dump task for databases that support migrations #1835 [Rick Olson] + +* Fixed script/profiler for Ruby 1.8.2 #1863 [Rick Olson] + +* Fixed clone_structure_to_test task for SQLite #1864 [jon@burningbush.us] + +* Added -m/--mime-types option to the WEBrick server, so you can specify a Apache-style mime.types file to load #2059 [ask@develooper.com] + +* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 [Kevin Clark] + +* Added -c/--charset option to WEBrick server, so you can specify a default charset (which without changes is UTF-8) #2084 [wejn@box.cz] + +* Make the default stats task extendable by modifying the STATS_DIRECTORIES constant + +* Allow the selected environment to define RAILS_DEFAULT_LOGGER, and have Rails::Initializer use it if it exists. + +* Moved all the shared tasks from Rakefile into Rails, so that the Rakefile is empty and doesn't require updating. + +* Added Rails::Initializer and Rails::Configuration to abstract all of the common setup out of config/environment.rb (uses config/boot.rb to bootstrap the initializer and paths) + +* Fixed the scaffold generator to fail right away if the database isn't accessible instead of in mid-air #1169 [Chad Fowler] + +* Corrected project-local generator location in scripts.rb #2010 [Michael Schuerig] + +* Don't require the environment just to clear the logs #2093 [Scott Barron] + +* Make the default rakefile read *.rake files from config/tasks (for easy extension of the rakefile by e.g. generators) + +* Only load breakpoint in development mode and when BREAKPOINT_SERVER_PORT is defined. + +* Allow the --toggle-spin switch on process/reaper to be negated + +* Replace render_partial with render :partial in scaffold generator [Nicholas Seckar] + +* Added -w flag to ps in process/reaper #1934 [Scott Barron] + +* Allow ERb in the database.yml file (just like with fixtures), so you can pull out the database configuration in environment variables #1822 [Duane Johnson] + +* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help. + +* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina Jr.] + +* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [Michael Schubert] + +* Dropped the 'immediate close-down' of FCGI processes since it didn't work consistently and produced bad responses when it didn't. So now a TERM ensures exit after the next request (just as if the process is handling a request when it receives the signal). This means that you'll have to 'nudge' all FCGI processes with a request in order to ensure that they have all reloaded. This can be done by something like ./script/process/repear --nudge 'http://www.myapp.com' --instances 10, which will load the myapp site 10 times (and thus hit all of the 10 FCGI processes once, enough to shut down). + + +*0.13.1* (11 July, 2005) + +* Look for app-specific generators in RAILS_ROOT/generators rather than the clunky old RAILS_ROOT/script/generators. Nobody really uses this feature except for the unit tests, so it's a negligible-impact change. If you want to work with third-party generators, drop them in ~/.rails/generators or simply install gems. + +* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson] + +* Added support for SQL Server in the database rake tasks #1652 [Ken Barker] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set. + +* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version + +* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay] + +* Improved documentation for environment config files #1625 [court3nay] + + +*0.13.0* (6 July, 2005) + +* Changed the default logging level in config/environment.rb to INFO for production (so SQL statements won't be logged) + +* Added migration generator: ./script/generate migration add_system_settings + +* Added "migrate" as rake task to execute all the pending migrations from db/migrate + +* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina Jr.] + +* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski] + +* SIGTERM also gracefully exits dispatch.fcgi. Ignore SIGUSR1 on Windows. + +* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi [Stefan Kaes] + +* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them. + +* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush) + +* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [Michael Schubert] + +* ActionMailer::Base subclasses are reloaded with the other rails components #1262 + +* Made the WEBrick adapter not use a mutex around action performance if ActionController::Base.allow_concurrency is true (default is false) + +* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron] + +* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski] + +* Fixed the test_helper.rb to be safe for requiring controllers from multiple spots, like app/controllers/article_controller.rb and app/controllers/admin/article_controller.rb, without reloading the environment twice #1390 [Nicholas Seckar] + +* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar] + +* Added -e/--environment option to script/runner #1408 [François Beausoleil] + +* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id]. #1367 + +* Added graceful exit from pressing CTRL-C during the run of the rails command #1150 [Caleb Tennis] + +* Allow graceful exits for dispatch.fcgi processes by sending a SIGUSR1. If the process is currently handling a request, the request will be allowed to complete and then will terminate itself. If a request is not being handled, the process is terminated immediately (via #exit). This basically works like restart graceful on Apache. [Jamis Buck] + +* Made dispatch.fcgi more robust by catching fluke errors and retrying unless its a permanent condition. [Jamis Buck] + +* Added console --profile for profiling an IRB session #1154 [Jeremy Kemper] + +* Changed console_sandbox into console --sandbox #1154 [Jeremy Kemper] + + +*0.12.1* (20th April, 2005) + +* Upgraded to Active Record 1.10.1, Action Pack 1.8.1, Action Mailer 0.9.1, Action Web Service 0.7.1 + + +*0.12.0* (19th April, 2005) + +* Fixed that purge_test_database would use database settings from the development environment when recreating the test database #1122 [rails@cogentdude.com] + +* Added script/benchmarker to easily benchmark one or more statement a number of times from within the environment. Examples: + + # runs the one statement 10 times + script/benchmarker 10 'Person.expensive_method(10)' + + # pits the two statements against each other with 50 runs each + script/benchmarker 50 'Person.expensive_method(10)' 'Person.cheap_method(10)' + +* Added script/profiler to easily profile a single statement from within the environment. Examples: + + script/profiler 'Person.expensive_method(10)' + script/profiler 'Person.expensive_method(10)' 10 # runs the statement 10 times + +* Added Rake target clear_logs that'll truncate all the *.log files in log/ to zero #1079 [Lucas Carlson] + +* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k@v2studio.com] + +* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson] + +* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4 + + +*0.11.1* (27th March, 2005) + +* Fixed the dispatch.fcgi use of a logger + +* Upgraded to Active Record 1.9.1, Action Pack 1.7.0, Action Mailer 0.8.1, Action Web Service 0.6.2, Active Support 1.0.3 + + +*0.11.0* (22th March, 2005) + +* Removed SCRIPT_NAME from the WEBrick environment to prevent conflicts with PATH_INFO #896 [Nicholas Seckar] + +* Removed ?$1 from the dispatch.f/cgi redirect line to get rid of 'complete/path/from/request.html' => nil being in the @params now that the ENV["REQUEST_URI"] is used to determine the path #895 [dblack/Nicholas Seckar] + +* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process + +* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Lütke] + +* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar] + +* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Lütke] + +* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples: + + ./script/runner 'ReminderService.deliver' + ./script/runner 'Mailer.receive(STDIN.read)' + + This makes it easier to do CRON and postfix scripts without actually making a script just to trigger 1 line of code. + +* Fixed webrick_server cookie handling to allow multiple cookes to be set at once #800, #813 [dave@cherryville.org] + +* Fixed the Rakefile's interaction with postgresql to: + + 1. Use PGPASSWORD and PGHOST in the environment to fix prompting for + passwords when connecting to a remote db and local socket connections. + 2. Add a '-x' flag to pg_dump which stops it dumping privileges #807 [rasputnik] + 3. Quote the user name and use template0 when dumping so the functions doesn't get dumped too #855 [pburleson] + 4. Use the port if available #875 [madrobby] + +* Upgraded to Active Record 1.9.0, Action Pack 1.6.0, Action Mailer 0.8.0, Action Web Service 0.6.1, Active Support 1.0.2 + + +*0.10.1* (7th March, 2005) + +* Fixed rake stats to ignore editor backup files like model.rb~ #791 [Sebastian Kanthak] + +* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Lütke] + +* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence) + +* Fixed that scaffold generator new template should use local variable instead of instance variable #778 [Dan Peterson] + +* Allow unit tests to run on a remote server for PostgreSQL #781 [adamm@galacticasoftware.com] + +* Added web_service generator (run ./script/generate web_service for help) #776 [Leon Bredt] + +* Added app/apis and components to code statistics report #729 [Scott Barron] + +* Fixed WEBrick server to use ABSOLUTE_RAILS_ROOT instead of working_directory #687 [Nicholas Seckar] + +* Fixed rails_generator to be usable without RubyGems #686 [Cristi BALAN] + +* Fixed -h/--help for generate and destroy generators #331 + +* Added begin/rescue around the FCGI dispatcher so no uncaught exceptions can bubble up to kill the process (logs to log/fastcgi.crash.log) + +* Fixed that association#count would produce invalid sql when called sequentialy #659 [kanis@comcard.de] + +* Fixed test/mocks/testing to the correct test/mocks/test #740 + +* Added early failure if the Ruby version isn't 1.8.2 or above #735 + +* Removed the obsolete -i/--index option from the WEBrick servlet #743 + +* Upgraded to Active Record 1.8.0, Action Pack 1.5.1, Action Mailer 0.7.1, Action Web Service 0.6.0, Active Support 1.0.1 + + +*0.10.0* (24th February, 2005) + +* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel Molina Jr.] + +* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar] + +* Upgraded to breakpoint 92 which fixes: + + * overload IRB.parse_opts(), fixes #443 + => breakpoints in tests work even when running them via rake + * untaint handlers, might fix an issue discussed on the Rails ML + * added verbose mode to breakpoint_client + * less noise caused by breakpoint_client by default + * ignored TerminateLineInput exception in signal handler + => quiet exit on Ctrl-C + +* Added support for independent components residing in /components. Example: + + Controller: components/list/items_controller.rb + (holds a List::ItemsController class with uses_component_template_root called) + + Model : components/list/item.rb + (namespace is still shared, so an Item model in app/models will take precedence) + + Views : components/list/items/show.rhtml + + +* Added --sandbox option to script/console that'll roll back all changes made to the database when you quit #672 [Jeremy Kemper] + +* Added 'recent' as a rake target that'll run tests for files that changed in the last 10 minutes #612 [Jeremy Kemper] + +* Changed script/console to default to development environment and drop --no-inspect #650 [Jeremy Kemper] + +* Added that the 'fixture :posts' syntax can be used for has_and_belongs_to_many fixtures where a model doesn't exist #572 [Jeremy Kemper] + +* Added that running test_units and test_functional now performs the clone_structure_to_test as well #566 [rasputnik] + +* Added new generator framework that informs about its doings on generation and enables updating and destruction of generated artifacts. See the new script/destroy and script/update for more details #487 [Jeremy Kemper] + +* Added Action Web Service as a new add-on framework for Action Pack [Leon Bredt] + +* Added Active Support as an independent utility and standard library extension bundle + +* Upgraded to Active Record 1.7.0, Action Pack 1.5.0, Action Mailer 0.7.0 + + +*0.9.5* (January 25th, 2005) + +* Fixed dependency reloading by switching to a remove_const approach where all Active Records, Active Record Observers, and Action Controllers are reloading by undefining their classes. This enables you to remove methods in all three types and see the change reflected immediately and it fixes #539. This also means that only those three types of classes will benefit from the const_missing and reloading approach. If you want other classes (like some in lib/) to reload, you must use require_dependency to do it. + +* Added Florian Gross' latest version of Breakpointer and friends that fixes a variaty of bugs #441 [Florian Gross] + +* Fixed skeleton Rakefile to work with sqlite3 out of the box #521 [rasputnik] + +* Fixed that script/breakpointer didn't get the Ruby path rewritten as the other scripts #523 [brandt@kurowski.net] + +* Fixed handling of syntax errors in models that had already been succesfully required once in the current interpreter + +* Fixed that models that weren't referenced in associations weren't being reloaded in the development mode by reinstating the reload + +* Fixed that generate scaffold would produce bad functional tests + +* Fixed that FCGI can also display SyntaxErrors + +* Upgraded to Active Record 1.6.0, Action Pack 1.4.0 + + +*0.9.4.1* (January 18th, 2005) + +* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina Jr.] + +* Fixed binding of caller #496 [Alexey] + +* Upgraded to Active Record 1.5.1, Action Pack 1.3.1, Action Mailer 0.6.1 + + +*0.9.4* (January 17th, 2005) + +* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Lütke] + +* Flipped code-to-test ratio around to be more readable #468 [Scott Baron] + +* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson] + +* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Lütke] + +* Added rewrite rules to deal with caching to public/.htaccess + +* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model. + +* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from + RAILS_ENV instead of just forcing development #424 [Tobias Lütke] + +* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [Chris McGrath] + +* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby + +* Removed Fancy Indexing as a default option on the WEBrick servlet as it made it harder to use various caching schemes + +* Upgraded to Active Record 1.5, Action Pack 1.3, Action Mailer 0.6 + + +*0.9.3* (January 4th, 2005) + +* Added support for SQLite in the auto-dumping/importing of schemas for development -> test #416 + +* Added automated rewriting of the shebang lines on installs through the gem rails command #379 [Manfred Stienstra] + +* Added ActionMailer::Base.deliver_method = :test to the test environment so that mail objects are available in ActionMailer::Base.deliveries + for functional testing. + +* Added protection for creating a model through the generators with a name of an existing class, like Thread or Date. + It'll even offer you a synonym using wordnet.princeton.edu as a look-up. No, I'm not kidding :) [Florian Gross] + +* Fixed dependency management to happen in a unified fashion for Active Record and Action Pack using the new Dependencies module. This means that + the environment options needs to change from: + + Before in development.rb: + ActionController::Base.reload_dependencies = true   + ActiveRecord::Base.reload_associations     = true + + Now in development.rb: + Dependencies.mechanism = :load + + Before in production.rb and test.rb: + ActionController::Base.reload_dependencies = false + ActiveRecord::Base.reload_associations     = false + + Now in production.rb and test.rb: + Dependencies.mechanism = :require + +* Fixed problems with dependency caching and controller hierarchies on Ruby 1.8.2 in development mode #351 + +* Fixed that generated action_mailers doesnt need to require the action_mailer since thats already done in the environment #382 [Lucas Carlson] + +* Upgraded to Action Pack 1.2.0 and Active Record 1.4.0 + + +*0.9.2* + +* Fixed CTRL-C exists from the Breakpointer to be a clean affair without error dumping [Kent Sibilev] + +* Fixed "rake stats" to work with sub-directories in models and controllers and to report the code to test ration [Scott Baron] + +* Added that Active Record associations are now reloaded instead of cleared to work with the new const_missing hook in Active Record. + +* Added graceful handling of an inaccessible log file by redirecting output to STDERR with a warning #330 [rainmkr] + +* Added support for a -h/--help parameter in the generator #331 [Ulysses] + +* Fixed that File.expand_path in config/environment.rb would fail when dealing with symlinked public directories [mjobin] + +* Upgraded to Action Pack 1.1.0 and Active Record 1.3.0 + + +*0.9.1* + +* Upgraded to Action Pack 1.0.1 for important bug fix + +* Updated gem dependencies + + +*0.9.0* + +* Renamed public/dispatch.servlet to script/server -- it wasn't really dispatching anyway as its delegating calls to public/dispatch.rb + +* Renamed AbstractApplicationController and abstract_application.rb to ApplicationController and application.rb, so that it will be possible + for the framework to automatically pick up on app/views/layouts/application.rhtml and app/helpers/application.rb + +* Added script/console that makes it even easier to start an IRB session for interacting with the domain model. Run with no-args to + see help. + +* Added breakpoint support through the script/breakpointer client. This means that you can break out of execution at any point in + the code, investigate and change the model, AND then resume execution! Example: + + class WeblogController < ActionController::Base + def index + @posts = Post.find_all + breakpoint "Breaking out from the list" + end + end + + So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window. + Here you can do things like: + + Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint' + + >> @posts.inspect + => "[#nil, \"body\"=>nil, \"id\"=>\"1\"}>, + #\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]" + >> @posts.first.title = "hello from a breakpoint" + => "hello from a breakpoint" + + ...and even better is that you can examine how your runtime objects actually work: + + >> f = @posts.first + => #nil, "body"=>nil, "id"=>"1"}> + >> f. + Display all 152 possibilities? (y or n) + + Finally, when you're ready to resume execution, you press CTRL-D + +* Changed environments to be configurable through an environment variable. By default, the environment is "development", but you + can change that and set your own by configuring the Apache vhost with a string like (mod_env must be available on the server): + + SetEnv RAILS_ENV production + + ...if you're using WEBrick, you can pick the environment to use with the command-line parameters -e/--environment, like this: + + ruby public/dispatcher.servlet -e production + +* Added a new default environment called "development", which leaves the production environment to be tuned exclusively for that. + +* Added a start_server in the root of the Rails application to make it even easier to get started + +* Fixed public/.htaccess to use RewriteBase and share the same rewrite rules for all the dispatch methods + +* Fixed webrick_server to handle requests in a serialized manner (the Rails reloading infrastructure is not thread-safe) + +* Added support for controllers in directories. So you can have: + + app/controllers/account_controller.rb # URL: /account/ + app/controllers/admin/account_controller.rb # URL: /admin/account/ + + NOTE: You need to update your public/.htaccess with the new rules to pick it up + +* Added reloading for associations and dependencies under cached environments like FastCGI and mod_ruby. This makes it possible to use + those environments for development. This is turned on by default, but can be turned off with + ActiveRecord::Base.reload_associations = false and ActionController::Base.reload_dependencies = false in production environments. + +* Added support for sub-directories in app/models. So now you can have something like Basecamp with: + + app/models/accounting + app/models/project + app/models/participants + app/models/settings + + It's poor man's namespacing, but only for file-system organization. You still require files just like before. + Nothing changes inside the files themselves. + + +* Fixed a few references in the tests generated by new_mailer [Jeremy Kemper] + +* Added support for mocks in testing with test/mocks + +* Cleaned up the environments a bit and added global constant RAILS_ROOT + + +*0.8.5* (9) + +* Made dev-util available to all tests, so you can insert breakpoints in any test case to get an IRB prompt at that point [Jeremy Kemper]: + + def test_complex_stuff + @david.projects << @new_project + breakpoint "Let's have a closer look at @david" + end + + You need to install dev-utils yourself for this to work ("gem install dev-util"). + +* Added shared generator behavior so future upgrades should be possible without manually copying over files [Jeremy Kemper] + +* Added the new helper style to both controller and helper templates [Jeremy Kemper] + +* Added new_crud generator for creating a model and controller at the same time with explicit scaffolding [Jeremy Kemper] + +* Added configuration of Test::Unit::TestCase.fixture_path to test_helper to concide with the new AR fixtures style + +* Fixed that new_model was generating singular table/fixture names + +* Upgraded to Action Mailer 0.4.0 + +* Upgraded to Action Pack 0.9.5 + +* Upgraded to Active Record 1.1.0 + + +*0.8.0 (15)* + +* Removed custom_table_name option for new_model now that the Inflector is as powerful as it is + +* Changed the default rake action to just do testing and separate API generation and coding statistics into a "doc" task. + +* Fixed WEBrick dispatcher to handle missing slashes in the URLs gracefully [alexey] + +* Added user option for all postgresql tool calls in the rakefile [elvstone] + +* Fixed problem with running "ruby public/dispatch.servlet" instead of "cd public; ruby dispatch.servlet" [alexey] + +* Fixed WEBrick server so that it no longer hardcodes the ruby interpreter used to "ruby" but will get the one used based + on the Ruby runtime configuration. [Marcel Molina Jr.] + +* Fixed Dispatcher so it'll route requests to magic_beans to MagicBeansController/magic_beans_controller.rb [Caio Chassot] + +* "new_controller MagicBeans" and "new_model SubscriptionPayments" will now both behave properly as they use the new Inflector. + +* Fixed problem with MySQL foreign key constraint checks in Rake :clone_production_structure_to_test target [Andreas Schwarz] + +* Changed WEBrick server to by default be auto-reloading, which is slower but makes source changes instant. + Class compilation cache can be turned on with "-c" or "--cache-classes". + +* Added "-b/--binding" option to WEBrick dispatcher to bind the server to a specific IP address (default: 127.0.0.1) [Kevin Temp] + +* dispatch.fcgi now DOESN'T set FCGI_PURE_RUBY as it was slowing things down for now reason [Andreas Schwarz] + +* Added new_mailer generator to work with Action Mailer + +* Included new framework: Action Mailer 0.3 + +* Upgraded to Action Pack 0.9.0 + +* Upgraded to Active Record 1.0.0 + + +*0.7.0* + +* Added an optional second argument to the new_model script that allows the programmer to specify the table name, + which will used to generate a custom table_name method in the model and will also be used in the creation of fixtures. + [Kevin Radloff] + +* script/new_model now turns AccountHolder into account_holder instead of accountholder [Kevin Radloff] + +* Fixed the faulty handleing of static files with WEBrick [Andreas Schwarz] + +* Unified function_test_helper and unit_test_helper into test_helper + +* Fixed bug with the automated production => test database dropping on PostgreSQL [dhawkins] + +* create_fixtures in both the functional and unit test helper now turns off the log during fixture generation + and can generate more than one fixture at a time. Which makes it possible for assignments like: + + @people, @projects, @project_access, @companies, @accounts = + create_fixtures "people", "projects", "project_access", "companies", "accounts" + +* Upgraded to Action Pack 0.8.5 (locally-scoped variables, partials, advanced send_file) + +* Upgraded to Active Record 0.9.5 (better table_name guessing, cloning, find_all_in_collection) + + +*0.6.5* + +* No longer specifies a template for rdoc, so it'll use whatever is default (you can change it in the rakefile) + +* The new_model generator will now use the same rules for plural wordings as Active Record + (so Category will give categories, not categorys) [Kevin Radloff] + +* dispatch.fcgi now sets FCGI_PURE_RUBY to true to ensure that it's the Ruby version that's loaded [danp] + +* Made the GEM work with Windows + +* Fixed bug where mod_ruby would "forget" the load paths added when switching between controllers + +* PostgreSQL are now supported for the automated production => test database dropping [Kevin Radloff] + +* Errors thrown by the dispatcher are now properly handled in FCGI. + +* Upgraded to Action Pack 0.8.0 (lots and lots and lots of fixes) + +* Upgraded to Active Record 0.9.4 (a bunch of fixes) + + +*0.6.0* + +* Added AbstractionApplicationController as a superclass for all controllers generated. This class can be used + to carry filters and methods that are to be shared by all. It has an accompanying ApplicationHelper that all + controllers will also automatically have available. + +* Added environments that can be included from any script to get the full Active Record and Action Controller + context running. This can be used by maintenance scripts or to interact with the model through IRB. Example: + + require 'config/environments/production' + + for account in Account.find_all + account.recalculate_interests + end + + A short migration script for an account model that had it's interest calculation strategy changed. + +* Accessing the index of a controller with "/weblog" will now redirect to "/weblog/" (only on Apache, not WEBrick) + +* Simplified the default Apache config so even remote requests are served off CGI as a default. + You'll now have to do something specific to activate mod_ruby and FCGI (like using the force urls). + This should make it easier for new comers that start on an external server. + +* Added more of the necessary Apache options to .htaccess to make it easier to setup + +* Upgraded to Action Pack 0.7.9 (lots of fixes) + +* Upgraded to Active Record 0.9.3 (lots of fixes) + + +*0.5.7* + +* Fixed bug in the WEBrick dispatcher that prevented it from getting parameters from the URL + (through GET requests or otherwise) + +* Added lib in root as a place to store app specific libraries + +* Added lib and vendor to load_path, so anything store within can be loaded directly. + Hence lib/redcloth.rb can be loaded with require "redcloth" + +* Upgraded to Action Pack 0.7.8 (lots of fixes) + +* Upgraded to Active Record 0.9.2 (minor upgrade) + + +*0.5.6* + +* Upgraded to Action Pack 0.7.7 (multipart form fix) + +* Updated the generated template stubs to valid XHTML files + +* Ensure that controllers generated are capitalized, so "new_controller TodoLists" + gives the same as "new_controller Todolists" and "new_controller todolists". + + +*0.5.5* + +* Works on Windows out of the box! (Dropped symlinks) + +* Added webrick dispatcher: Try "ruby public/dispatch.servlet --help" [Florian Gross] + +* Report errors about initialization to browser (instead of attempting to use uninitialized logger) + +* Upgraded to Action Pack 0.7.6 + +* Upgraded to Active Record 0.9.1 + +* Added distinct 500.html instead of reusing 404.html + +* Added MIT license + + +*0.5.0* + +* First public release diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/README b/crowbar/change-image/dell/openstack_manager/vendor/railties/README new file mode 100644 index 00000000000..37ec8ea2110 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/README @@ -0,0 +1,243 @@ +== Welcome to Rails + +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. + +This pattern splits the view (also called the presentation) into "dumb" templates +that are primarily responsible for inserting pre-built data in between HTML tags. +The model contains the "smart" domain objects (such as Account, Product, Person, +Post) that holds all the business logic and knows how to persist themselves to +a database. The controller handles the incoming requests (such as Save New Account, +Update Product, Show Post) by manipulating the model and directing data to the view. + +In Rails, the model is handled by what's called an object-relational mapping +layer entitled Active Record. This layer allows you to present the data from +database rows as objects and embellish these data objects with business logic +methods. You can read more about Active Record in +link:files/vendor/rails/activerecord/README.html. + +The controller and view are handled by the Action Pack, which handles both +layers by its two parts: Action View and Action Controller. These two layers +are bundled in a single package due to their heavy interdependence. This is +unlike the relationship between the Active Record and Action Pack that is much +more separate. Each of these packages can be used independently outside of +Rails. You can read more about Action Pack in +link:files/vendor/rails/actionpack/README.html. + + +== Getting Started + +1. At the command prompt, start a new Rails application using the rails command + and your application name. Ex: rails myapp +2. Change directory into myapp and start the web server: script/server (run with --help for options) +3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!" +4. Follow the guidelines to start developing your application + + +== Web Servers + +By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails +with a variety of other web servers. + +Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is +suitable for development and deployment of Rails applications. If you have Ruby Gems installed, +getting up and running with mongrel is as easy as: gem install mongrel. +More info at: http://mongrel.rubyforge.org + +Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or +Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use +FCGI or proxy to a pack of Mongrels/Thin/Ebb servers. + +== Apache .htaccess example for FCGI/CGI + +# General Apache options +AddHandler fastcgi-script .fcgi +AddHandler cgi-script .cgi +Options +FollowSymLinks +ExecCGI + +# If you don't want Rails to look in certain directories, +# use the following rewrite rules so that Apache won't rewrite certain requests +# +# Example: +# RewriteCond %{REQUEST_URI} ^/notrails.* +# RewriteRule .* - [L] + +# Redirect all requests not available on the filesystem to Rails +# By default the cgi dispatcher is used which is very slow +# +# For better performance replace the dispatcher with the fastcgi one +# +# Example: +# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] +RewriteEngine On + +# If your Rails application is accessed via an Alias directive, +# then you MUST also set the RewriteBase in this htaccess file. +# +# Example: +# Alias /myrailsapp /path/to/myrailsapp/public +# RewriteBase /myrailsapp + +RewriteRule ^$ index.html [QSA] +RewriteRule ^([^.]+)$ $1.html [QSA] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ dispatch.cgi [QSA,L] + +# In case Rails experiences terminal errors +# Instead of displaying this message you can supply a file here which will be rendered instead +# +# Example: +# ErrorDocument 500 /500.html + +ErrorDocument 500 "

        Application error

        Rails application failed to start properly" + + +== Debugging Rails + +Sometimes your application goes wrong. Fortunately there are a lot of tools that +will help you debug it and get it back on the rails. + +First area to check is the application log files. Have "tail -f" commands running +on the server.log and development.log. Rails will automatically display debugging +and runtime information to these files. Debugging info will also be shown in the +browser on requests from 127.0.0.1. + +You can also log your own messages directly into the log file from your code using +the Ruby logger class from inside your controllers. Example: + + class WeblogController < ActionController::Base + def destroy + @weblog = Weblog.find(params[:id]) + @weblog.destroy + logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") + end + end + +The result will be a message in your log file along the lines of: + + Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1 + +More information on how to use the logger is at http://www.ruby-doc.org/core/ + +Also, Ruby documentation can be found at http://www.ruby-lang.org/ including: + +* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/ +* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) + +These two online (and free) books will bring you up to speed on the Ruby language +and also on programming in general. + + +== Debugger + +Debugger support is available through the debugger command when you start your Mongrel or +Webrick server with --debugger. This means that you can break out of execution at any point +in the code, investigate and change the model, AND then resume execution! +You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug' +Example: + + class WeblogController < ActionController::Base + def index + @posts = Post.find(:all) + debugger + end + end + +So the controller will accept the action, run the first line, then present you +with a IRB prompt in the server window. Here you can do things like: + + >> @posts.inspect + => "[#nil, \"body\"=>nil, \"id\"=>\"1\"}>, + #\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]" + >> @posts.first.title = "hello from a debugger" + => "hello from a debugger" + +...and even better is that you can examine how your runtime objects actually work: + + >> f = @posts.first + => #nil, "body"=>nil, "id"=>"1"}> + >> f. + Display all 152 possibilities? (y or n) + +Finally, when you're ready to resume execution, you enter "cont" + + +== Console + +You can interact with the domain model by starting the console through script/console. +Here you'll have all parts of the application configured, just like it is when the +application is running. You can inspect domain models, change values, and save to the +database. Starting the script without arguments will launch it in the development environment. +Passing an argument will specify a different environment, like script/console production. + +To reload your controllers and models after launching the console run reload! + +== dbconsole + +You can go to the command line of your database directly through script/dbconsole. +You would be connected to the database with the credentials defined in database.yml. +Starting the script without arguments will connect you to the development database. Passing an +argument will connect you to a different database, like script/dbconsole production. +Currently works for mysql, postgresql and sqlite. + +== Description of Contents + +app + Holds all the code that's specific to this particular application. + +app/controllers + Holds controllers that should be named like weblogs_controller.rb for + automated URL mapping. All controllers should descend from ApplicationController + which itself descends from ActionController::Base. + +app/models + Holds models that should be named like post.rb. + Most models will descend from ActiveRecord::Base. + +app/views + Holds the template files for the view that should be named like + weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby + syntax. + +app/views/layouts + Holds the template files for layouts to be used with views. This models the common + header/footer method of wrapping views. In your views, define a layout using the + layout :default and create a file named default.html.erb. Inside default.html.erb, + call <% yield %> to render the view using this layout. + +app/helpers + Holds view helpers that should be named like weblogs_helper.rb. These are generated + for you automatically when using script/generate for controllers. Helpers can be used to + wrap functionality for your views into methods. + +config + Configuration files for the Rails environment, the routing map, the database, and other dependencies. + +db + Contains the database schema in schema.rb. db/migrate contains all + the sequence of Migrations for your schema. + +doc + This directory is where your application documentation will be stored when generated + using rake doc:app + +lib + Application specific libraries. Basically, any kind of custom code that doesn't + belong under controllers, models, or helpers. This directory is in the load path. + +public + The directory available for the web server. Contains subdirectories for images, stylesheets, + and javascripts. Also contains the dispatchers and the default HTML files. This should be + set as the DOCUMENT_ROOT of your web server. + +script + Helper scripts for automation and generation. + +test + Unit and functional tests along with fixtures. When using the script/generate scripts, template + test files will be generated for you and placed in this directory. + +vendor + External libraries that the application depends on. Also includes the plugins subdirectory. + If the app has frozen rails, those gems also go here, under vendor/rails/. + This directory is in the load path. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/Rakefile b/crowbar/change-image/dell/openstack_manager/vendor/railties/Rakefile new file mode 100644 index 00000000000..198c1bbed01 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/Rakefile @@ -0,0 +1,368 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' +require 'rake/gempackagetask' + +require 'date' +require 'rbconfig' + +require File.join(File.dirname(__FILE__), 'lib/rails', 'version') + +PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' +PKG_NAME = 'rails' +PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD +PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" +PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}" + +RELEASE_NAME = "REL #{PKG_VERSION}" + +RUBY_FORGE_PROJECT = "rails" +RUBY_FORGE_USER = "webster132" + + +task :default => :test + +## This is required until the regular test task +## below passes. It's not ideal, but at least +## we can see the failures +task :test do + Dir['test/**/*_test.rb'].all? do |file| + ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) + system(ruby, '-Itest', file) + end or raise "Failures" +end + +Rake::TestTask.new("regular_test") do |t| + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.warning = true + t.verbose = true +end + + +BASE_DIRS = %w( + app + config/environments + config/initializers + config/locales + db + doc + log + lib + lib/tasks + public + script + script/performance + test + vendor + vendor/plugins + tmp/sessions + tmp/cache + tmp/sockets + tmp/pids +) + +APP_DIRS = %w( models controllers helpers views views/layouts ) +PUBLIC_DIRS = %w( images javascripts stylesheets ) +TEST_DIRS = %w( fixtures unit functional mocks mocks/development mocks/test ) + +LOG_FILES = %w( server.log development.log test.log production.log ) +HTML_FILES = %w( 422.html 404.html 500.html index.html robots.txt favicon.ico images/rails.png + javascripts/prototype.js javascripts/application.js + javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js ) +BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler runner server plugin ) + +VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport activeresource railties ) + + +desc "Generates a fresh Rails package with documentation" +task :fresh_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content, :generate_documentation ] + +desc "Generates a fresh Rails package using GEMs with documentation" +task :fresh_gem_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_ties_content, :copy_gem_environment ] + +desc "Generates a fresh Rails package without documentation (faster)" +task :fresh_rails_without_docs => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content ] + +desc "Generates a fresh Rails package without documentation (faster)" +task :fresh_rails_without_docs_using_links => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ] + +desc "Generates minimal Rails package using symlinks" +task :dev => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ] + +desc "Packages the fresh Rails package with documentation" +task :package => [ :clean, :fresh_rails ] do + system %{cd ..; tar -czvf #{PKG_NAME}-#{PKG_VERSION}.tgz #{PKG_NAME}} + system %{cd ..; zip -r #{PKG_NAME}-#{PKG_VERSION}.zip #{PKG_NAME}} +end + +task :clean do + rm_rf PKG_DESTINATION +end + +# Get external spinoffs ------------------------------------------------------------------- + +desc "Updates railties to the latest version of the javascript spinoffs" +task :update_js do + for js in %w( prototype controls dragdrop effects ) + rm "html/javascripts/#{js}.js" + cp "./../actionpack/lib/action_view/helpers/javascripts/#{js}.js", "html/javascripts" + end +end + +# Make directory structure ---------------------------------------------------------------- + +def make_dest_dirs(dirs, path = '.') + mkdir_p dirs.map { |dir| File.join(PKG_DESTINATION, path.to_s, dir) } +end + +desc "Make the directory structure for the new Rails application" +task :make_dir_structure => [ :make_base_dirs, :make_app_dirs, :make_public_dirs, :make_test_dirs ] + +task(:make_base_dirs) { make_dest_dirs BASE_DIRS } +task(:make_app_dirs) { make_dest_dirs APP_DIRS, 'app' } +task(:make_public_dirs) { make_dest_dirs PUBLIC_DIRS, 'public' } +task(:make_test_dirs) { make_dest_dirs TEST_DIRS, 'test' } + + +# Initialize file stubs ------------------------------------------------------------------- + +desc "Initialize empty file stubs (such as for logging)" +task :initialize_file_stubs => [ :initialize_log_files ] + +task :initialize_log_files do + log_dir = File.join(PKG_DESTINATION, 'log') + chmod 0777, log_dir + LOG_FILES.each do |log_file| + log_path = File.join(log_dir, log_file) + touch log_path + chmod 0666, log_path + end +end + + +# Copy Vendors ---------------------------------------------------------------------------- + +desc "Copy in all the Rails packages to vendor" +task :copy_vendor_libraries do + mkdir File.join(PKG_DESTINATION, 'vendor', 'rails') + VENDOR_LIBS.each { |dir| cp_r File.join('..', dir), File.join(PKG_DESTINATION, 'vendor', 'rails', dir) } + FileUtils.rm_r(Dir.glob(File.join(PKG_DESTINATION, 'vendor', 'rails', "**", ".svn"))) +end + +desc "Link in all the Rails packages to vendor" +task :link_vendor_libraries do + mkdir File.join(PKG_DESTINATION, 'vendor', 'rails') + VENDOR_LIBS.each { |dir| ln_s "/usr/share/rails/#{dir}", File.join(PKG_DESTINATION, 'vendor', 'rails', dir) } +end + + +# Copy Ties Content ----------------------------------------------------------------------- + +desc "Make copies of all the default content of ties" +task :copy_ties_content => [ + :copy_rootfiles, :copy_dispatches, :copy_html_files, :copy_application, + :copy_configs, :copy_binfiles, :copy_test_helpers, :copy_app_doc_readme ] + +task :copy_dispatches do + copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.rb") + chmod 0755, "#{PKG_DESTINATION}/public/dispatch.rb" + + copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.cgi") + chmod 0755, "#{PKG_DESTINATION}/public/dispatch.cgi" + + copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi") + chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi" +end + +task :copy_html_files do + HTML_FILES.each { |file| cp File.join('html', file), File.join(PKG_DESTINATION, 'public', file) } +end + +task :copy_application do + cp "helpers/application_controller.rb", "#{PKG_DESTINATION}/app/controllers/application_controller.rb" + cp "helpers/application_helper.rb", "#{PKG_DESTINATION}/app/helpers/application_helper.rb" +end + +task :copy_configs do + app_name = "rails" + socket = nil + require 'erb' + File.open("#{PKG_DESTINATION}/config/database.yml", 'w') {|f| f.write ERB.new(IO.read("configs/databases/sqlite3.yml"), nil, '-').result(binding)} + + cp "configs/routes.rb", "#{PKG_DESTINATION}/config/routes.rb" + + cp "configs/initializers/backtrace_silencers.rb", "#{PKG_DESTINATION}/config/initializers/backtrace_silencers.rb" + cp "configs/initializers/inflections.rb", "#{PKG_DESTINATION}/config/initializers/inflections.rb" + cp "configs/initializers/mime_types.rb", "#{PKG_DESTINATION}/config/initializers/mime_types.rb" + cp "configs/initializers/new_rails_defaults.rb", "#{PKG_DESTINATION}/config/initializers/new_rails_defaults.rb" + + cp "configs/locales/en.yml", "#{PKG_DESTINATION}/config/locales/en.yml" + + cp "configs/seeds.rb", "#{PKG_DESTINATION}/db/seeds.rb" + + cp "environments/boot.rb", "#{PKG_DESTINATION}/config/boot.rb" + File.open("#{PKG_DESTINATION}/config/environment.rb", 'w') {|f| f.write ERB.new(IO.read("environments/environment.rb"), nil, '-').result(binding)} + cp "environments/production.rb", "#{PKG_DESTINATION}/config/environments/production.rb" + cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb" + cp "environments/test.rb", "#{PKG_DESTINATION}/config/environments/test.rb" + +end + +task :copy_binfiles do + BIN_FILES.each do |file| + dest_file = File.join(PKG_DESTINATION, 'script', file) + copy_with_rewritten_ruby_path(File.join('bin', file), dest_file) + chmod 0755, dest_file + end +end + +task :copy_rootfiles do + cp "fresh_rakefile", "#{PKG_DESTINATION}/Rakefile" + cp "README", "#{PKG_DESTINATION}/README" + cp "CHANGELOG", "#{PKG_DESTINATION}/CHANGELOG" +end + +task :copy_test_helpers do + cp "helpers/test_helper.rb", "#{PKG_DESTINATION}/test/test_helper.rb" +end + +task :copy_app_doc_readme do + cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP" +end + +def copy_with_rewritten_ruby_path(src_file, dest_file) + ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + + File.open(dest_file, 'w') do |df| + File.open(src_file) do |sf| + line = sf.gets + if (line =~ /#!.+ruby\s*/) != nil + df.puts("#!#{ruby}") + else + df.puts(line) + end + df.write(sf.read) + end + end +end + +desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"' +task :generate_guides do + ruby "guides/rails_guides.rb" +end + + +# Generate documentation ------------------------------------------------------------------ + +desc "Generate documentation for the framework and for the empty application" +task :generate_documentation => [ :generate_app_doc, :generate_rails_framework_doc ] + +task :generate_rails_framework_doc do + system %{cd #{PKG_DESTINATION}; rake doc:rails} +end + +task :generate_app_doc do + cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP" + system %{cd #{PKG_DESTINATION}; rake doc:app} +end + +Rake::RDocTask.new { |rdoc| + rdoc.rdoc_dir = 'doc' + rdoc.title = "Railties -- Gluing the Engine to the Rails" + rdoc.options << '--line-numbers' << '--inline-source' << '--accessor' << 'cattr_accessor=object' + rdoc.options << '--charset' << 'utf-8' + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' + rdoc.rdoc_files.include('README', 'CHANGELOG') + rdoc.rdoc_files.include('lib/*.rb') + rdoc.rdoc_files.include('lib/rails/*.rb') + rdoc.rdoc_files.include('lib/rails_generator/*.rb') + rdoc.rdoc_files.include('lib/commands/**/*.rb') +} + +# Generate GEM ---------------------------------------------------------------------------- + +task :copy_gem_environment do + cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb" + chmod 0755, dest_file +end + + +PKG_FILES = FileList[ + '[a-zA-Z]*', + 'bin/**/*', + 'builtin/**/*', + 'configs/**/*', + 'doc/**/*', + 'dispatches/**/*', + 'environments/**/*', + 'guides/**/*', + 'helpers/**/*', + 'generators/**/*', + 'html/**/*', + 'lib/**/*' +] - [ 'test' ] + +spec = Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'rails' + s.version = PKG_VERSION + s.summary = "Web-application framework with template engine, control-flow layer, and ORM." + s.description = <<-EOF + Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick + on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates. + EOF + + s.add_dependency('rake', '>= 0.8.3') + s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.5' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.5' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.5' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.5' + PKG_BUILD) + + s.rdoc_options << '--exclude' << '.' + s.has_rdoc = false + + s.files = PKG_FILES.to_a.delete_if {|f| f =~ %r{\.svn|guides/output}} + s.require_path = 'lib' + s.bindir = "bin" # Use these for applications. + s.executables = ["rails"] + s.default_executable = "rails" + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "rails" +end + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + + +# Publishing ------------------------------------------------------- +desc "Publish the rails gem" +task :pgem => [:gem] do + Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload + `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` +end + +desc "Publish the guides" +task :pguides => :generate_guides do + mkdir_p 'pkg' + `tar -czf pkg/guides.gz guides/output` + Rake::SshFilePublisher.new("web.rubyonrails.org", "/u/sites/guides.rubyonrails.org/public", "pkg", "guides.gz").upload + `ssh web.rubyonrails.org 'cd /u/sites/guides.rubyonrails.org/public/ && tar -xvzf guides.gz && mv guides/output/* . && rm -rf guides*'` +end + +desc "Publish the release files to RubyForge." +task :release => [ :package ] do + require 'rake/contrib/rubyforgepublisher' + require 'rubyforge' + + packages = %w( gem ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" } + + rubyforge = RubyForge.new + rubyforge.login + rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages) +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/about b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/about new file mode 100644 index 00000000000..1eeb6eb9159 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/about @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" +require 'commands/about' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/console b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/console new file mode 100644 index 00000000000..235a1f27804 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/console @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/console' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/dbconsole b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/dbconsole new file mode 100644 index 00000000000..83c8436a9d9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/dbconsole @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/dbconsole' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/destroy b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/destroy new file mode 100644 index 00000000000..88d295f7aa0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/destroy @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/destroy' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/generate b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/generate new file mode 100644 index 00000000000..62a8a4c0c53 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/generate @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/generate' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/performance/benchmarker b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/performance/benchmarker new file mode 100644 index 00000000000..3bff809fb39 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/performance/benchmarker @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../../config/boot', __FILE__) +require 'commands/performance/benchmarker' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/performance/profiler b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/performance/profiler new file mode 100644 index 00000000000..07640575cd6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/performance/profiler @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../../config/boot', __FILE__) +require 'commands/performance/profiler' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/plugin b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/plugin new file mode 100644 index 00000000000..b82201fa833 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/plugin @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/plugin' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/rails b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/rails new file mode 100644 index 00000000000..6a0c675206c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/rails @@ -0,0 +1,20 @@ +require File.dirname(__FILE__) + '/../lib/ruby_version_check' +Signal.trap("INT") { puts; exit } + +require File.dirname(__FILE__) + '/../lib/rails/version' +if %w(--version -v).include? ARGV.first + puts "Rails #{Rails::VERSION::STRING}" + exit(0) +end + +freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) } + +app_path = ARGV.first + +require File.dirname(__FILE__) + '/../lib/rails_generator' + +require 'rails_generator/scripts/generate' +Rails::Generator::Base.use_application_sources! +Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app') + +Dir.chdir(app_path) { `rake rails:freeze:gems`; puts "froze" } if freeze \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/runner b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/runner new file mode 100644 index 00000000000..be4c5d4572e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/runner @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/runner' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/server b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/server new file mode 100644 index 00000000000..b9fcb717935 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/bin/server @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.expand_path('../../config/boot', __FILE__) +require 'commands/server' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info.rb new file mode 100644 index 00000000000..8c858d23fb3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info.rb @@ -0,0 +1,131 @@ +module Rails + module Info + mattr_accessor :properties + class << (@@properties = []) + def names + map &:first + end + + def value_for(property_name) + if property = assoc(property_name) + property.last + end + end + end + + class << self #:nodoc: + def property(name, value = nil) + value ||= yield + properties << [name, value] if value + rescue Exception + end + + def frameworks + %w( active_record action_pack active_resource action_mailer active_support ) + end + + def framework_version(framework) + if Object.const_defined?(framework.classify) + require "#{framework}/version" + "#{framework.classify}::VERSION::STRING".constantize + end + end + + def edge_rails_revision(info = git_info) + info[/commit ([a-z0-9-]+)/, 1] || freeze_edge_version + end + + def freeze_edge_version + if File.exist?(rails_vendor_root) + begin + Dir[File.join(rails_vendor_root, 'REVISION_*')].first.scan(/_(\d+)$/).first.first + rescue + Dir[File.join(rails_vendor_root, 'TAG_*')].first.scan(/_(.+)$/).first.first rescue 'unknown' + end + end + end + + def to_s + column_width = properties.names.map {|name| name.length}.max + ["About your application's environment", *properties.map do |property| + "%-#{column_width}s %s" % property + end] * "\n" + end + + alias inspect to_s + + def to_html + returning table = '' do + properties.each do |(name, value)| + table << %() + table << %() + end + table << '
        #{CGI.escapeHTML(name.to_s)}#{CGI.escapeHTML(value.to_s)}
        ' + end + end + + protected + def rails_vendor_root + @rails_vendor_root ||= "#{RAILS_ROOT}/vendor/rails" + end + + def git_info + env_lang, ENV['LC_ALL'] = ENV['LC_ALL'], 'C' + Dir.chdir(rails_vendor_root) do + silence_stderr { `git log -n 1` } + end + ensure + ENV['LC_ALL'] = env_lang + end + end + + # The Ruby version and platform, e.g. "1.8.2 (powerpc-darwin8.2.0)". + property 'Ruby version', "#{RUBY_VERSION} (#{RUBY_PLATFORM})" + + # The RubyGems version, if it's installed. + property 'RubyGems version' do + Gem::RubyGemsVersion + end + + property 'Rack version' do + ::Rack.release + end + + # The Rails version. + property 'Rails version' do + Rails::VERSION::STRING + end + + # Versions of each Rails framework (Active Record, Action Pack, + # Active Resource, Action Mailer, and Active Support). + frameworks.each do |framework| + property "#{framework.titlecase} version" do + framework_version(framework) + end + end + + # The Rails Git revision, if it's checked out into vendor/rails. + property 'Edge Rails revision' do + edge_rails_revision + end + + # The application's location on the filesystem. + property 'Application root' do + File.expand_path(RAILS_ROOT) + end + + # The current Rails environment (development, test, or production). + property 'Environment' do + RAILS_ENV + end + + # The name of the database adapter for the current environment. + property 'Database adapter' do + ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] + end + + property 'Database schema version' do + ActiveRecord::Migrator.current_version rescue nil + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info_controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info_controller.rb new file mode 100644 index 00000000000..05745d606d2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info_controller.rb @@ -0,0 +1,9 @@ +class Rails::InfoController < ActionController::Base + def properties + if consider_all_requests_local || local_request? + render :inline => Rails::Info.to_html + else + render :text => '

        For security purposes, this information is only available to local requests.

        ', :status => 500 + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info_helper.rb new file mode 100644 index 00000000000..e5605a8d9be --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails/info_helper.rb @@ -0,0 +1,2 @@ +module Rails::InfoHelper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails_info_controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails_info_controller.rb new file mode 100644 index 00000000000..2009eb3a994 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/builtin/rails_info/rails_info_controller.rb @@ -0,0 +1,2 @@ +# Alias to ensure old public.html still works. +RailsInfoController = Rails::InfoController diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/frontbase.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/frontbase.yml new file mode 100644 index 00000000000..c0c3588be1b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/frontbase.yml @@ -0,0 +1,28 @@ +# FrontBase versions 4.x +# +# Get the bindings: +# gem install ruby-frontbase + +development: + adapter: frontbase + host: localhost + database: <%= app_name %>_development + username: <%= app_name %> + password: '' + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: frontbase + host: localhost + database: <%= app_name %>_test + username: <%= app_name %> + password: '' + +production: + adapter: frontbase + host: localhost + database: <%= app_name %>_production + username: <%= app_name %> + password: '' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/ibm_db.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/ibm_db.yml new file mode 100644 index 00000000000..a9716ddb445 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/ibm_db.yml @@ -0,0 +1,62 @@ +# IBM Dataservers +# +# Home Page +# http://rubyforge.org/projects/rubyibm/ +# +# To install the ibm_db gem: +# On Linux: +# Source the db2profile file and set the necessary environment variables: +# +# . /home/db2inst1/sqllib/db2profile +# export IBM_DB_DIR=/opt/ibm/db2/V9.1 +# export IBM_DB_LIB=/opt/ibm/db2/V9.1/lib32 +# +# Then issue the command: gem install ibm_db +# +# On Windows: +# Issue the command: gem install ibm_db +# If prompted, select the mswin32 option +# +# For more details on the installation refer to http://rubyforge.org/docman/view.php/2361/7682/IBM_DB_GEM.pdf +# +# For more details on the connection parameters below refer to: +# http://rubyibm.rubyforge.org/docs/adapter/0.9.0/rdoc/classes/ActiveRecord/ConnectionAdapters/IBM_DBAdapter.html + +development: + adapter: ibm_db + username: db2inst1 + password: + database: <%= app_name[0,4] %>_dev + #schema: db2inst1 + #host: localhost + #port: 50000 + #account: my_account + #app_user: my_app_user + #application: my_application + #workstation: my_workstation + +test: + adapter: ibm_db + username: db2inst1 + password: + database: <%= app_name[0,4] %>_tst + #schema: db2inst1 + #host: localhost + #port: 50000 + #account: my_account + #app_user: my_app_user + #application: my_application + #workstation: my_workstation + +production: + adapter: ibm_db + username: db2inst1 + password: + database: <%= app_name[0,4] %>_prd + #schema: db2inst1 + #host: localhost + #port: 50000 + #account: my_account + #app_user: my_app_user + #application: my_application + #workstation: my_workstation \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/mysql.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/mysql.yml new file mode 100644 index 00000000000..1a14bfb332f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/mysql.yml @@ -0,0 +1,60 @@ +# MySQL. Versions 4.1 and 5.0 are recommended. +# +# Install the MySQL driver: +# gem install mysql +# On Mac OS X: +# sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql +# On Mac OS X Leopard: +# sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config +# This sets the ARCHFLAGS environment variable to your native architecture +# On Windows: +# gem install mysql +# Choose the win32 build. +# Install MySQL and put its /bin directory on your path. +# +# And be sure to use new-style password hashing: +# http://dev.mysql.com/doc/refman/5.0/en/old-client.html +development: + adapter: mysql + encoding: utf8 + reconnect: false + database: <%= app_name %>_development + pool: 5 + username: root + password: +<% if socket -%> + socket: <%= socket %> +<% else -%> + host: localhost +<% end -%> + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: mysql + encoding: utf8 + reconnect: false + database: <%= app_name %>_test + pool: 5 + username: root + password: +<% if socket -%> + socket: <%= socket %> +<% else -%> + host: localhost +<% end -%> + +production: + adapter: mysql + encoding: utf8 + reconnect: false + database: <%= app_name %>_production + pool: 5 + username: root + password: +<% if socket -%> + socket: <%= socket %> +<% else -%> + host: localhost +<% end -%> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/oracle.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/oracle.yml new file mode 100644 index 00000000000..a1883f62560 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/oracle.yml @@ -0,0 +1,39 @@ +# Oracle/OCI 8i, 9, 10g +# +# Requires Ruby/OCI8: +# http://rubyforge.org/projects/ruby-oci8/ +# +# Specify your database using any valid connection syntax, such as a +# tnsnames.ora service name, or a SQL connect url string of the form: +# +# //host:[port][/service name] +# +# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And +# until true bind variables are supported, cursor_sharing is set by default +# to 'similar'. Both can be changed in the configation below; the defaults +# are equivalent to specifying: +# +# prefetch_rows: 100 +# cursor_sharing: similar +# + +development: + adapter: oracle + database: <%= app_name %>_development + username: <%= app_name %> + password: + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: oracle + database: <%= app_name %>_test + username: <%= app_name %> + password: + +production: + adapter: oracle + database: <%= app_name %>_production + username: <%= app_name %> + password: diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/postgresql.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/postgresql.yml new file mode 100644 index 00000000000..f600e054cfd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/postgresql.yml @@ -0,0 +1,51 @@ +# PostgreSQL. Versions 7.4 and 8.x are supported. +# +# Install the ruby-postgres driver: +# gem install ruby-postgres +# On Mac OS X: +# gem install ruby-postgres -- --include=/usr/local/pgsql +# On Windows: +# gem install ruby-postgres +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +development: + adapter: postgresql + encoding: unicode + database: <%= app_name %>_development + pool: 5 + username: <%= app_name %> + password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # The server defaults to notice. + #min_messages: warning + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: postgresql + encoding: unicode + database: <%= app_name %>_test + pool: 5 + username: <%= app_name %> + password: + +production: + adapter: postgresql + encoding: unicode + database: <%= app_name %>_production + pool: 5 + username: <%= app_name %> + password: diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/sqlite2.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/sqlite2.yml new file mode 100644 index 00000000000..46f01cb42c9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/sqlite2.yml @@ -0,0 +1,19 @@ +# SQLite version 2.x +# gem install sqlite-ruby +development: + adapter: sqlite + database: db/development.sqlite2 + pool: 5 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: sqlite + database: db/test.sqlite2 + pool: 5 + +production: + adapter: sqlite + database: db/production.sqlite2 + pool: 5 diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/sqlite3.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/sqlite3.yml new file mode 100644 index 00000000000..025d62a8d81 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/databases/sqlite3.yml @@ -0,0 +1,22 @@ +# SQLite version 3.x +# gem install sqlite3-ruby (not necessary on OS X Leopard) +development: + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 + +production: + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 + timeout: 5000 diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/empty.log b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/empty.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/backtrace_silencers.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/backtrace_silencers.rb new file mode 100644 index 00000000000..c2169ed01c5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying do debug a problem that might steem from framework code. +# Rails.backtrace_cleaner.remove_silencers! \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/inflections.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/inflections.rb new file mode 100644 index 00000000000..d531b8bb825 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/inflections.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format +# (all these examples are active by default): +# ActiveSupport::Inflector.inflections do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/mime_types.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/mime_types.rb new file mode 100644 index 00000000000..72aca7e441e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/mime_types.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf +# Mime::Type.register_alias "text/html", :iphone diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/new_rails_defaults.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/new_rails_defaults.rb new file mode 100644 index 00000000000..c94db0a6642 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/new_rails_defaults.rb @@ -0,0 +1,21 @@ +# Be sure to restart your server when you modify this file. + +# These settings change the behavior of Rails 2 apps and will be defaults +# for Rails 3. You can remove this initializer when Rails 3 is released. + +if defined?(ActiveRecord) + # Include Active Record class name as root for JSON serialized output. + ActiveRecord::Base.include_root_in_json = true + + # Store the full class name (including module namespace) in STI type column. + ActiveRecord::Base.store_full_sti_class = true +end + +ActionController::Routing.generate_best_match = false + +# Use ISO 8601 format for JSON serialized times and dates. +ActiveSupport.use_standard_json_time_format = true + +# Don't escape HTML entities in JSON, leave that for the #json_escape helper. +# if you're including raw json in an HTML page. +ActiveSupport.escape_html_entities_in_json = false \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/session_store.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/session_store.rb new file mode 100644 index 00000000000..4499ab84b60 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/initializers/session_store.rb @@ -0,0 +1,15 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key for verifying cookie session data integrity. +# If you change this key, all old sessions will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +ActionController::Base.session = { + :key => '_<%= app_name %>_session', + :secret => '<%= app_secret %>' +} + +# Use the database for sessions instead of the cookie-based default, +# which shouldn't be used to store highly confidential information +# (create the session table with "rake db:sessions:create") +# ActionController::Base.session_store = :active_record_store diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/locales/en.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/locales/en.yml new file mode 100644 index 00000000000..f265c068d89 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/locales/en.yml @@ -0,0 +1,5 @@ +# Sample localization file for English. Add more files in this directory for other locales. +# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. + +en: + hello: "Hello world" \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/routes.rb new file mode 100644 index 00000000000..ea14ce1bfcc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/routes.rb @@ -0,0 +1,43 @@ +ActionController::Routing::Routes.draw do |map| + # The priority is based upon order of creation: first created -> highest priority. + + # Sample of regular route: + # map.connect 'products/:id', :controller => 'catalog', :action => 'view' + # Keep in mind you can assign values other than :controller and :action + + # Sample of named route: + # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' + # This route can be invoked with purchase_url(:id => product.id) + + # Sample resource route (maps HTTP verbs to controller actions automatically): + # map.resources :products + + # Sample resource route with options: + # map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get } + + # Sample resource route with sub-resources: + # map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller + + # Sample resource route with more complex sub-resources + # map.resources :products do |products| + # products.resources :comments + # products.resources :sales, :collection => { :recent => :get } + # end + + # Sample resource route within a namespace: + # map.namespace :admin do |admin| + # # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb) + # admin.resources :products + # end + + # You can have the root of your site routed with map.root -- just remember to delete public/index.html. + # map.root :controller => "welcome" + + # See how all your routes lay out with "rake routes" + + # Install the default routes as the lowest priority. + # Note: These default routes make all actions in every controller accessible via GET requests. You should + # consider removing or commenting them out if you're using named routes and resources. + map.connect ':controller/:action/:id' + map.connect ':controller/:action/:id.:format' +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/seeds.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/seeds.rb new file mode 100644 index 00000000000..3174d0cb8a8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/configs/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) +# Major.create(:name => 'Daley', :city => cities.first) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/config.ru b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/config.ru new file mode 100644 index 00000000000..acbfe4e9ae1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/config.ru @@ -0,0 +1,7 @@ +# Rack Dispatcher + +# Require your environment file to bootstrap Rails +require File.dirname(__FILE__) + '/config/environment' + +# Dispatch the request +run ActionController::Dispatcher.new diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/dispatch.fcgi b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/dispatch.fcgi new file mode 100644 index 00000000000..664dbbbee82 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/dispatch.fcgi @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +# +# You may specify the path to the FastCGI crash log (a log of unhandled +# exceptions which forced the FastCGI instance to exit, great for debugging) +# and the number of requests to process before running garbage collection. +# +# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log +# and the GC period is nil (turned off). A reasonable number of requests +# could range from 10-100 depending on the memory footprint of your app. +# +# Example: +# # Default log path, normal GC behavior. +# RailsFCGIHandler.process! +# +# # Default log path, 50 requests between GC. +# RailsFCGIHandler.process! nil, 50 +# +# # Custom log path, normal GC behavior. +# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log' +# +require File.dirname(__FILE__) + "/../config/environment" +require 'fcgi_handler' + +RailsFCGIHandler.process! diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/dispatch.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/dispatch.rb new file mode 100644 index 00000000000..32fa3b2665a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/dispatch.rb @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) + +# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: +# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired +require "dispatcher" + +ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) +Dispatcher.dispatch diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/gateway.cgi b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/gateway.cgi new file mode 100644 index 00000000000..0305b7f810f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/dispatches/gateway.cgi @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby + +require 'drb' + +# This file includes an experimental gateway CGI implementation. It will work +# only on platforms which support both fork and sockets. +# +# To enable it edit public/.htaccess and replace dispatch.cgi with gateway.cgi. +# +# Next, create the directory log/drb_gateway and grant the apache user rw access +# to said directory. +# +# On the next request to your server, the gateway tracker should start up, along +# with a few listener processes. This setup should provide you with much better +# speeds than dispatch.cgi. +# +# Keep in mind that the first request made to the server will be slow, as the +# tracker and listeners will have to load. Also, the tracker and listeners will +# shutdown after a period if inactivity. You can set this value below -- the +# default is 90 seconds. + +TrackerSocket = File.expand_path(File.join(File.dirname(__FILE__), '../log/drb_gateway/tracker.sock')) +DieAfter = 90 # Seconds +Listeners = 3 + +def message(s) + $stderr.puts "gateway.cgi: #{s}" if ENV && ENV["DEBUG_GATEWAY"] +end + +def listener_socket(number) + File.expand_path(File.join(File.dirname(__FILE__), "../log/drb_gateway/listener_#{number}.sock")) +end + +unless File.exist? TrackerSocket + message "Starting tracker and #{Listeners} listeners" + fork do + Process.setsid + STDIN.reopen "/dev/null" + STDOUT.reopen "/dev/null", "a" + + root = File.expand_path(File.dirname(__FILE__) + '/..') + + message "starting tracker" + fork do + ARGV.clear + ARGV << TrackerSocket << Listeners.to_s << DieAfter.to_s + load File.join(root, 'script', 'tracker') + end + + message "starting listeners" + require File.join(root, 'config/environment.rb') + Listeners.times do |number| + fork do + ARGV.clear + ARGV << listener_socket(number) << DieAfter.to_s + load File.join(root, 'script', 'listener') + end + end + end + + message "waiting for tracker and listener to arise..." + ready = false + 10.times do + sleep 0.5 + break if (ready = File.exist?(TrackerSocket) && File.exist?(listener_socket(0))) + end + + if ready + message "tracker and listener are ready" + else + message "Waited 5 seconds, listener and tracker not ready... dropping request" + Kernel.exit 1 + end +end + +DRb.start_service + +message "connecting to tracker" +tracker = DRbObject.new_with_uri("drbunix:#{TrackerSocket}") + +input = $stdin.read +$stdin.close + +env = ENV.inspect + +output = nil +tracker.with_listener do |number| + message "connecting to listener #{number}" + socket = listener_socket(number) + listener = DRbObject.new_with_uri("drbunix:#{socket}") + output = listener.process(env, input) + message "listener #{number} has finished, writing output" +end + +$stdout.write output +$stdout.flush +$stdout.close diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/doc/README_FOR_APP b/crowbar/change-image/dell/openstack_manager/vendor/railties/doc/README_FOR_APP new file mode 100644 index 00000000000..fe41f5cc24d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/doc/README_FOR_APP @@ -0,0 +1,2 @@ +Use this README file to introduce your application and point to useful places in the API for learning more. +Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/boot.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/boot.rb new file mode 100644 index 00000000000..dd5e3b69164 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/boot.rb @@ -0,0 +1,110 @@ +# Don't change this file! +# Configure your app in config/environment.rb and config/environments/*.rb + +RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) + +module Rails + class << self + def boot! + unless booted? + preinitialize + pick_boot.run + end + end + + def booted? + defined? Rails::Initializer + end + + def pick_boot + (vendor_rails? ? VendorBoot : GemBoot).new + end + + def vendor_rails? + File.exist?("#{RAILS_ROOT}/vendor/rails") + end + + def preinitialize + load(preinitializer_path) if File.exist?(preinitializer_path) + end + + def preinitializer_path + "#{RAILS_ROOT}/config/preinitializer.rb" + end + end + + class Boot + def run + load_initializer + Rails::Initializer.run(:set_load_path) + end + end + + class VendorBoot < Boot + def load_initializer + require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" + Rails::Initializer.run(:install_gem_spec_stubs) + Rails::GemDependency.add_frozen_gem_path + end + end + + class GemBoot < Boot + def load_initializer + self.class.load_rubygems + load_rails_gem + require 'initializer' + end + + def load_rails_gem + if version = self.class.gem_version + gem 'rails', version + else + gem 'rails' + end + rescue Gem::LoadError => load_error + $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) + exit 1 + end + + class << self + def rubygems_version + Gem::RubyGemsVersion rescue nil + end + + def gem_version + if defined? RAILS_GEM_VERSION + RAILS_GEM_VERSION + elsif ENV.include?('RAILS_GEM_VERSION') + ENV['RAILS_GEM_VERSION'] + else + parse_gem_version(read_environment_rb) + end + end + + def load_rubygems + min_version = '1.3.2' + require 'rubygems' + unless rubygems_version >= min_version + $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.) + exit 1 + end + + rescue LoadError + $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org) + exit 1 + end + + def parse_gem_version(text) + $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/ + end + + private + def read_environment_rb + File.read("#{RAILS_ROOT}/config/environment.rb") + end + end + end +end + +# All that for this: +Rails.boot! diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/development.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/development.rb new file mode 100644 index 00000000000..85c9a6080ea --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/development.rb @@ -0,0 +1,17 @@ +# Settings specified here will take precedence over those in config/environment.rb + +# In the development environment your application's code is reloaded on +# every request. This slows down response time but is perfect for development +# since you don't have to restart the webserver when you make code changes. +config.cache_classes = false + +# Log error messages when you accidentally call methods on nil. +config.whiny_nils = true + +# Show full error reports and disable caching +config.action_controller.consider_all_requests_local = true +config.action_view.debug_rjs = true +config.action_controller.perform_caching = false + +# Don't care if the mailer can't send +config.action_mailer.raise_delivery_errors = false \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/environment.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/environment.rb new file mode 100644 index 00000000000..4a2df363073 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/environment.rb @@ -0,0 +1,41 @@ +# Be sure to restart your server when you modify this file + +# Specifies gem version of Rails to use when vendor/rails is not present +<%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION + +# Bootstrap the Rails environment, frameworks, and default configuration +require File.join(File.dirname(__FILE__), 'boot') + +Rails::Initializer.run do |config| + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Add additional load paths for your own custom dirs + # config.load_paths += %W( #{RAILS_ROOT}/extras ) + + # Specify gems that this application depends on and have them installed with rake gems:install + # config.gem "bj" + # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" + # config.gem "sqlite3-ruby", :lib => "sqlite3" + # config.gem "aws-s3", :lib => "aws/s3" + + # Only load the plugins named here, in the order given (default is alphabetical). + # :all can be used as a placeholder for all plugins not explicitly named + # config.plugins = [ :exception_notification, :ssl_requirement, :all ] + + # Skip frameworks you're not going to use. To use Rails without a database, + # you must remove the Active Record framework. + # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] + + # Activate observers that should always be running + # config.active_record.observers = :cacher, :garbage_collector, :forum_observer + + # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. + # Run "rake -D time" for a list of tasks for finding time zone names. + config.time_zone = 'UTC' + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] + # config.i18n.default_locale = :de +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/production.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/production.rb new file mode 100644 index 00000000000..27119d2d18b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/production.rb @@ -0,0 +1,28 @@ +# Settings specified here will take precedence over those in config/environment.rb + +# The production environment is meant for finished, "live" apps. +# Code is not reloaded between requests +config.cache_classes = true + +# Full error reports are disabled and caching is turned on +config.action_controller.consider_all_requests_local = false +config.action_controller.perform_caching = true +config.action_view.cache_template_loading = true + +# See everything in the log (default is :info) +# config.log_level = :debug + +# Use a different logger for distributed setups +# config.logger = SyslogLogger.new + +# Use a different cache store in production +# config.cache_store = :mem_cache_store + +# Enable serving of images, stylesheets, and javascripts from an asset server +# config.action_controller.asset_host = "http://assets.example.com" + +# Disable delivery errors, bad email addresses will be ignored +# config.action_mailer.raise_delivery_errors = false + +# Enable threaded mode +# config.threadsafe! \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/test.rb new file mode 100644 index 00000000000..d6f80a4080d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/environments/test.rb @@ -0,0 +1,28 @@ +# Settings specified here will take precedence over those in config/environment.rb + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! +config.cache_classes = true + +# Log error messages when you accidentally call methods on nil. +config.whiny_nils = true + +# Show full error reports and disable caching +config.action_controller.consider_all_requests_local = true +config.action_controller.perform_caching = false +config.action_view.cache_template_loading = true + +# Disable request forgery protection in test environment +config.action_controller.allow_forgery_protection = false + +# Tell Action Mailer not to deliver emails to the real world. +# The :test delivery method accumulates sent emails in the +# ActionMailer::Base.deliveries array. +config.action_mailer.delivery_method = :test + +# Use SQL instead of Active Record's schema dumper when creating the test database. +# This is necessary if your schema can't be completely dumped by the schema dumper, +# like if you have constraints or database-specific column types +# config.active_record.schema_format = :sql \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/fresh_rakefile b/crowbar/change-image/dell/openstack_manager/vendor/railties/fresh_rakefile new file mode 100644 index 00000000000..3bb0e8592a4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/fresh_rakefile @@ -0,0 +1,10 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require(File.join(File.dirname(__FILE__), 'config', 'boot')) + +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +require 'tasks/rails' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/application_controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/application_controller.rb new file mode 100644 index 00000000000..6635a3f487a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/application_controller.rb @@ -0,0 +1,10 @@ +# Filters added to this controller apply to all controllers in the application. +# Likewise, all the methods added will be available for all controllers. + +class ApplicationController < ActionController::Base + helper :all # include all helpers, all the time + protect_from_forgery # See ActionController::RequestForgeryProtection for details + + # Scrub sensitive parameters from your log + # filter_parameter_logging :password +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/application_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/application_helper.rb new file mode 100644 index 00000000000..22a7940eb21 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/application_helper.rb @@ -0,0 +1,3 @@ +# Methods added to this helper will be available to all templates in the application. +module ApplicationHelper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/performance_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/performance_test.rb new file mode 100644 index 00000000000..4b60558b439 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/performance_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' +require 'performance_test_help' + +# Profiling results for each test method are written to tmp/performance. +class BrowsingTest < ActionController::PerformanceTest + def test_homepage + get '/' + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/test_helper.rb new file mode 100644 index 00000000000..b9fe2517c84 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/helpers/test_helper.rb @@ -0,0 +1,38 @@ +ENV["RAILS_ENV"] = "test" +require File.expand_path(File.dirname(__FILE__) + "/../config/environment") +require 'test_help' + +class ActiveSupport::TestCase + # Transactional fixtures accelerate your tests by wrapping each test method + # in a transaction that's rolled back on completion. This ensures that the + # test database remains unchanged so your fixtures don't have to be reloaded + # between every test method. Fewer database queries means faster tests. + # + # Read Mike Clark's excellent walkthrough at + # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting + # + # Every Active Record database supports transactions except MyISAM tables + # in MySQL. Turn off transactional fixtures in this case; however, if you + # don't care one way or the other, switching from MyISAM to InnoDB tables + # is recommended. + # + # The only drawback to using transactional fixtures is when you actually + # need to test transactions. Since your test is bracketed by a transaction, + # any transactions started in your code will be automatically rolled back. + self.use_transactional_fixtures = true + + # Instantiated fixtures are slow, but give you @david where otherwise you + # would need people(:david). If you don't want to migrate your existing + # test cases which use the @david style and don't mind the speed hit (each + # instantiated fixtures translates to a database query per test method), + # then set this back to true. + self.use_instantiated_fixtures = false + + # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. + # + # Note: You'll currently still have to declare fixtures explicitly in integration tests + # -- they do not yet inherit this setting + fixtures :all + + # Add more helper methods to be used by all tests here... +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/404.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/404.html new file mode 100644 index 00000000000..eff660b90c3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/404.html @@ -0,0 +1,30 @@ + + + + + + + The page you were looking for doesn't exist (404) + + + + + +
        +

        The page you were looking for doesn't exist.

        +

        You may have mistyped the address or the page may have moved.

        +
        + + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/422.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/422.html new file mode 100644 index 00000000000..b54e4a3cadb --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/422.html @@ -0,0 +1,30 @@ + + + + + + + The change you wanted was rejected (422) + + + + + +
        +

        The change you wanted was rejected.

        +

        Maybe you tried to change something you didn't have access to.

        +
        + + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/500.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/500.html new file mode 100644 index 00000000000..ec3bbf02c43 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/500.html @@ -0,0 +1,30 @@ + + + + + + + We're sorry, but something went wrong (500) + + + + + +
        +

        We're sorry, but something went wrong.

        +

        We've been notified about this issue and we'll take a look at it shortly.

        +
        + + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/favicon.ico b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/favicon.ico new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/images/rails.png b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/images/rails.png new file mode 100644 index 00000000000..d5edc04e65f Binary files /dev/null and b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/images/rails.png differ diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/index.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/index.html new file mode 100644 index 00000000000..0dd5189fb7d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/index.html @@ -0,0 +1,275 @@ + + + + + Ruby on Rails: Welcome aboard + + + + + + +
        + + +
        + + + + +
        +

        Getting started

        +

        Here’s how to get rolling:

        + +
          +
        1. +

          Use script/generate to create your models and controllers

          +

          To see all available options, run it without parameters.

          +
        2. + +
        3. +

          Set up a default route and remove or rename this file

          +

          Routes are set up in config/routes.rb.

          +
        4. + +
        5. +

          Create your database

          +

          Run rake db:migrate to create your database. If you're not using SQLite (the default), edit config/database.yml with your username and password.

          +
        6. +
        +
        +
        + + +
        + + \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/application.js b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/application.js new file mode 100644 index 00000000000..fe4577696b2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/application.js @@ -0,0 +1,2 @@ +// Place your application-specific JavaScript functions and classes here +// This file is automatically included by javascript_include_tag :defaults diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/controls.js b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/controls.js new file mode 100644 index 00000000000..ca29aefdd1f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/controls.js @@ -0,0 +1,963 @@ +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2008 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 = Class.create({ + 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; + this.oldElementValue = this.element.value; + + 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); + // Force carriage returns as token delimiters anyway + if (!this.options.tokens.include('\n')) + this.options.tokens.push('\n'); + + 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, 'keydown', this.onKeyPress.bindAsEventListener(this)); + }, + + 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(); + Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + 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 = $(selectedElement).select('.' + this.options.select) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var bounds = this.getTokenBounds(); + if (bounds[0] != -1) { + var newValue = this.element.value.substr(0, bounds[0]); + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value + this.element.value.substr(bounds[1]); + } else { + this.element.value = value; + } + this.oldElementValue = this.element.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; + this.tokenBounds = null; + if(this.getToken().length>=this.options.minChars) { + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + this.oldElementValue = this.element.value; + }, + + getToken: function() { + var bounds = this.getTokenBounds(); + return this.element.value.substring(bounds[0], bounds[1]).strip(); + }, + + getTokenBounds: function() { + if (null != this.tokenBounds) return this.tokenBounds; + var value = this.element.value; + if (value.strip().empty()) return [-1, 0]; + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); + var offset = (diff == this.oldElementValue.length ? 1 : 0); + var prevTokenPos = -1, nextTokenPos = value.length; + var tp; + for (var index = 0, l = this.options.tokens.length; index < l; ++index) { + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); + if (tp > prevTokenPos) prevTokenPos = tp; + tp = value.indexOf(this.options.tokens[index], diff + offset); + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; + } + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); + } +}); + +Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { + var boundary = Math.min(newS.length, oldS.length); + for (var index = 0; index < boundary; ++index) + if (newS[index] != oldS[index]) + return index; + return boundary; +}; + +Ajax.Autocompleter = Class.create(Autocompleter.Base, { + 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.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 and collection editor +// Full rewrite by Christophe Porteneuve (April 2007). + +// 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({ + initialize: function(element, url, options) { + this.url = url; + this.element = element = $(element); + this.prepareOptions(); + this._controls = { }; + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! + Object.extend(this.options, options || { }); + if (!this.options.formId && this.element.id) { + this.options.formId = this.element.id + '-inplaceeditor'; + if ($(this.options.formId)) + this.options.formId = ''; + } + if (this.options.externalControl) + this.options.externalControl = $(this.options.externalControl); + if (!this.options.externalControl) + this.options.externalControlOnly = false; + this._originalBackground = this.element.getStyle('background-color') || 'transparent'; + this.element.title = this.options.clickToEditText; + this._boundCancelHandler = this.handleFormCancellation.bind(this); + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); + this._boundFailureHandler = this.handleAJAXFailure.bind(this); + this._boundSubmitHandler = this.handleFormSubmission.bind(this); + this._boundWrapperHandler = this.wrapUp.bind(this); + this.registerListeners(); + }, + checkForEscapeOrReturn: function(e) { + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; + if (Event.KEY_ESC == e.keyCode) + this.handleFormCancellation(e); + else if (Event.KEY_RETURN == e.keyCode) + this.handleFormSubmission(e); + }, + createControl: function(mode, handler, extraClasses) { + var control = this.options[mode + 'Control']; + var text = this.options[mode + 'Text']; + if ('button' == control) { + var btn = document.createElement('input'); + btn.type = 'submit'; + btn.value = text; + btn.className = 'editor_' + mode + '_button'; + if ('cancel' == mode) + btn.onclick = this._boundCancelHandler; + this._form.appendChild(btn); + this._controls[mode] = btn; + } else if ('link' == control) { + var link = document.createElement('a'); + link.href = '#'; + link.appendChild(document.createTextNode(text)); + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; + link.className = 'editor_' + mode + '_link'; + if (extraClasses) + link.className += ' ' + extraClasses; + this._form.appendChild(link); + this._controls[mode] = link; + } + }, + createEditField: function() { + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); + var fld; + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { + fld = document.createElement('input'); + fld.type = 'text'; + var size = this.options.size || this.options.cols || 0; + if (0 < size) fld.size = size; + } else { + fld = document.createElement('textarea'); + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); + fld.cols = this.options.cols || 40; + } + fld.name = this.options.paramName; + fld.value = text; // No HTML breaks conversion anymore + fld.className = 'editor_field'; + if (this.options.submitOnBlur) + fld.onblur = this._boundSubmitHandler; + this._controls.editor = fld; + if (this.options.loadTextURL) + this.loadExternalText(); + this._form.appendChild(this._controls.editor); + }, + createForm: function() { + var ipe = this; + function addText(mode, condition) { + var text = ipe.options['text' + mode + 'Controls']; + if (!text || condition === false) return; + ipe._form.appendChild(document.createTextNode(text)); + }; + this._form = $(document.createElement('form')); + this._form.id = this.options.formId; + this._form.addClassName(this.options.formClassName); + this._form.onsubmit = this._boundSubmitHandler; + this.createEditField(); + if ('textarea' == this._controls.editor.tagName.toLowerCase()) + this._form.appendChild(document.createElement('br')); + if (this.options.onFormCustomization) + this.options.onFormCustomization(this, this._form); + addText('Before', this.options.okControl || this.options.cancelControl); + this.createControl('ok', this._boundSubmitHandler); + addText('Between', this.options.okControl && this.options.cancelControl); + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); + addText('After', this.options.okControl || this.options.cancelControl); + }, + destroy: function() { + if (this._oldInnerHTML) + this.element.innerHTML = this._oldInnerHTML; + this.leaveEditMode(); + this.unregisterListeners(); + }, + enterEditMode: function(e) { + if (this._saving || this._editing) return; + this._editing = true; + this.triggerCallback('onEnterEditMode'); + if (this.options.externalControl) + this.options.externalControl.hide(); + this.element.hide(); + this.createForm(); + this.element.parentNode.insertBefore(this._form, this.element); + if (!this.options.loadTextURL) + this.postProcessEditField(); + if (e) Event.stop(e); + }, + enterHover: function(e) { + if (this.options.hoverClassName) + this.element.addClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onEnterHover'); + }, + getText: function() { + return this.element.innerHTML.unescapeHTML(); + }, + handleAJAXFailure: function(transport) { + this.triggerCallback('onFailure', transport); + if (this._oldInnerHTML) { + this.element.innerHTML = this._oldInnerHTML; + this._oldInnerHTML = null; + } + }, + handleFormCancellation: function(e) { + this.wrapUp(); + if (e) Event.stop(e); + }, + handleFormSubmission: function(e) { + var form = this._form; + var value = $F(this._controls.editor); + this.prepareSubmission(); + var params = this.options.callback(form, value) || ''; + if (Object.isString(params)) + params = params.toQueryParams(); + params.editorId = this.element.id; + if (this.options.htmlResponse) { + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Updater({ success: this.element }, this.url, options); + } else { + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.url, options); + } + if (e) Event.stop(e); + }, + leaveEditMode: function() { + this.element.removeClassName(this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + if (this.options.externalControl) + this.options.externalControl.show(); + this._saving = false; + this._editing = false; + this._oldInnerHTML = null; + this.triggerCallback('onLeaveEditMode'); + }, + leaveHover: function(e) { + if (this.options.hoverClassName) + this.element.removeClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onLeaveHover'); + }, + loadExternalText: function() { + this._form.addClassName(this.options.loadingClassName); + this._controls.editor.disabled = true; + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._form.removeClassName(this.options.loadingClassName); + var text = transport.responseText; + if (this.options.stripLoadedTextTags) + text = text.stripTags(); + this._controls.editor.value = text; + this._controls.editor.disabled = false; + this.postProcessEditField(); + }.bind(this), + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + postProcessEditField: function() { + var fpc = this.options.fieldPostCreation; + if (fpc) + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); + }, + prepareOptions: function() { + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); + [this._extraDefaultOptions].flatten().compact().each(function(defs) { + Object.extend(this.options, defs); + }.bind(this)); + }, + prepareSubmission: function() { + this._saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + registerListeners: function() { + this._listeners = { }; + var listener; + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { + listener = this[pair.value].bind(this); + this._listeners[pair.key] = listener; + if (!this.options.externalControlOnly) + this.element.observe(pair.key, listener); + if (this.options.externalControl) + this.options.externalControl.observe(pair.key, listener); + }.bind(this)); + }, + removeForm: function() { + if (!this._form) return; + this._form.remove(); + this._form = null; + this._controls = { }; + }, + showSaving: function() { + this._oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + this.element.addClassName(this.options.savingClassName); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + }, + triggerCallback: function(cbName, arg) { + if ('function' == typeof this.options[cbName]) { + this.options[cbName](this, arg); + } + }, + unregisterListeners: function() { + $H(this._listeners).each(function(pair) { + if (!this.options.externalControlOnly) + this.element.stopObserving(pair.key, pair.value); + if (this.options.externalControl) + this.options.externalControl.stopObserving(pair.key, pair.value); + }.bind(this)); + }, + wrapUp: function(transport) { + this.leaveEditMode(); + // Can't use triggerCallback due to backward compatibility: requires + // binding + direct element + this._boundComplete(transport, this.element); + } +}); + +Object.extend(Ajax.InPlaceEditor.prototype, { + dispose: Ajax.InPlaceEditor.prototype.destroy +}); + +Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { + initialize: function($super, element, url, options) { + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; + $super(element, url, options); + }, + + createEditField: function() { + var list = document.createElement('select'); + list.name = this.options.paramName; + list.size = 1; + this._controls.editor = list; + this._collection = this.options.collection || []; + if (this.options.loadCollectionURL) + this.loadCollection(); + else + this.checkForExternalText(); + this._form.appendChild(this._controls.editor); + }, + + loadCollection: function() { + this._form.addClassName(this.options.loadingClassName); + this.showLoadingText(this.options.loadingCollectionText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + var js = transport.responseText.strip(); + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check + throw('Server returned an invalid collection representation.'); + this._collection = eval(js); + this.checkForExternalText(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadCollectionURL, options); + }, + + showLoadingText: function(text) { + this._controls.editor.disabled = true; + var tempOption = this._controls.editor.firstChild; + if (!tempOption) { + tempOption = document.createElement('option'); + tempOption.value = ''; + this._controls.editor.appendChild(tempOption); + tempOption.selected = true; + } + tempOption.update((text || '').stripScripts().stripTags()); + }, + + checkForExternalText: function() { + this._text = this.getText(); + if (this.options.loadTextURL) + this.loadExternalText(); + else + this.buildOptionList(); + }, + + loadExternalText: function() { + this.showLoadingText(this.options.loadingText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._text = transport.responseText.strip(); + this.buildOptionList(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + + buildOptionList: function() { + this._form.removeClassName(this.options.loadingClassName); + this._collection = this._collection.map(function(entry) { + return 2 === entry.length ? entry : [entry, entry].flatten(); + }); + var marker = ('value' in this.options) ? this.options.value : this._text; + var textFound = this._collection.any(function(entry) { + return entry[0] == marker; + }.bind(this)); + this._controls.editor.update(''); + var option; + this._collection.each(function(entry, index) { + option = document.createElement('option'); + option.value = entry[0]; + option.selected = textFound ? entry[0] == marker : 0 == index; + option.appendChild(document.createTextNode(entry[1])); + this._controls.editor.appendChild(option); + }.bind(this)); + this._controls.editor.disabled = false; + Field.scrollFreeActivate(this._controls.editor); + } +}); + +//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** +//**** This only exists for a while, in order to let **** +//**** users adapt to the new API. Read up on the new **** +//**** API and convert your code to it ASAP! **** + +Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { + if (!options) return; + function fallback(name, expr) { + if (name in options || expr === undefined) return; + options[name] = expr; + }; + fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : + options.cancelLink == options.cancelButton == false ? false : undefined))); + fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : + options.okLink == options.okButton == false ? false : undefined))); + fallback('highlightColor', options.highlightcolor); + fallback('highlightEndColor', options.highlightendcolor); +}; + +Object.extend(Ajax.InPlaceEditor, { + DefaultOptions: { + ajaxOptions: { }, + autoRows: 3, // Use when multi-line w/ rows == 1 + cancelControl: 'link', // 'link'|'button'|false + cancelText: 'cancel', + clickToEditText: 'Click to edit', + externalControl: null, // id|elt + externalControlOnly: false, + fieldPostCreation: 'activate', // 'activate'|'focus'|false + formClassName: 'inplaceeditor-form', + formId: null, // id|elt + highlightColor: '#ffff99', + highlightEndColor: '#ffffff', + hoverClassName: '', + htmlResponse: true, + loadingClassName: 'inplaceeditor-loading', + loadingText: 'Loading...', + okControl: 'button', // 'link'|'button'|false + okText: 'ok', + paramName: 'value', + rows: 1, // If 1 and multi-line, uses autoRows + savingClassName: 'inplaceeditor-saving', + savingText: 'Saving...', + size: 0, + stripLoadedTextTags: false, + submitOnBlur: false, + textAfterControls: '', + textBeforeControls: '', + textBetweenControls: '' + }, + DefaultCallbacks: { + callback: function(form) { + return Form.serialize(form); + }, + onComplete: function(transport, element) { + // For backward compatibility, this one is bound to the IPE, and passes + // the element directly. It was too often customized, so we don't break it. + new Effect.Highlight(element, { + startcolor: this.options.highlightColor, keepBackgroundImage: true }); + }, + onEnterEditMode: null, + onEnterHover: function(ipe) { + ipe.element.style.backgroundColor = ipe.options.highlightColor; + if (ipe._effect) + ipe._effect.cancel(); + }, + onFailure: function(transport, ipe) { + alert('Error communication with the server: ' + transport.responseText.stripTags()); + }, + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. + onLeaveEditMode: null, + onLeaveHover: function(ipe) { + ipe._effect = new Effect.Highlight(ipe.element, { + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, + restorecolor: ipe._originalBackground, keepBackgroundImage: true + }); + } + }, + Listeners: { + click: 'enterEditMode', + keydown: 'checkForEscapeOrReturn', + mouseover: 'enterHover', + mouseout: 'leaveHover' + } +}); + +Ajax.InPlaceCollectionEditor.DefaultOptions = { + loadingCollectionText: 'Loading options...' +}; + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create({ + 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)); + } +}); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/dragdrop.js b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/dragdrop.js new file mode 100644 index 00000000000..07229f986f5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/dragdrop.js @@ -0,0 +1,973 @@ +// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2008 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(Object.isUndefined(Effect)) + 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(Object.isArray(containment)) { + 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 drop, affected = []; + + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) + drop = Droppables.findDeepestChild(affected); + + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); + if (drop) { + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + if (drop != this.last_active) 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({ + 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 = Object.isNumber(element._opacity) ? 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] || Object.isUndefined(arguments[1].endeffect)) + 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 && Object.isString(options.handle)) + 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.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(!Object.isUndefined(Draggable._dragging[this.element]) && + 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.delta) + this.delta = this.currentDelta(); + + 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); + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) + 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) { + if (!this._originallyAbsolute) + Position.relativize(this.element); + delete this._originallyAbsolute; + 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 && Object.isFunction(revert)) 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(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*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 }; + } +}); + +Draggable._dragging = { }; + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create({ + 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){ + element = $(element); + var s = Sortable.sortables[element.id]; + + 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).select('.' + 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')]; +}; \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/effects.js b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/effects.js new file mode 100644 index 00000000000..5a639d2dea9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/effects.js @@ -0,0 +1,1128 @@ +// Copyright (c) 2005-2008 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) { } +}; + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + .5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; + }, + pulse: function(pos, pulses) { + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, + tagifyText: function(element) { + 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( + new Element('span', {style: tagifyStyle}).update( + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if (((typeof element == 'object') || + Object.isFunction(element)) && + (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); + } +}; + +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(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 = Object.isString(effect.options.queue) ? + 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 = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + '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 (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#'; + } +}); + +Effect.Parallel = Class.create(Effect.Base, { + 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.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { + 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(Effect.Base, { + 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() { + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if (this.options.mode == 'absolute') { + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + '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(Effect.Base, { + 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 = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + '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(Effect.Base, { + 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+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()); } + ); +}; + +/* ------------- 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 options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance, y: 0, duration: split, 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'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + 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().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, 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] || { }, + oldOpacity = element.getInlineOpacity(), + transition = options.transition || Effect.Transitions.linear, + reverser = function(pos){ + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); + }; + + 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(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.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 + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: 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 ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, 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.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '
        '; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); + + return styleRules; +}; + +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { + results[property] = css[property]; + return results; + }); + if (!styles.opacity) styles.opacity = element.getOpacity(); + return styles; + }; +} + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element); + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + }; + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods); \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/prototype.js b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/prototype.js new file mode 100644 index 00000000000..9fe6e1243bc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/javascripts/prototype.js @@ -0,0 +1,4874 @@ +/* Prototype JavaScript framework, version 1.6.1 + * (c) 2005-2009 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.6.1', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile.*Safari/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + SelectorsAPI: !!document.querySelector, + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'); + var form = document.createElement('form'); + var isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +var Abstract = { }; + + +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; + } +}; + +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0; i < properties.length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames().first() == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = method.valueOf.bind(method); + value.toString = method.toString.bind(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString; + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(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 (isElement(object)) return; + + var results = []; + for (var property in object) { + var value = toJSON(object[property]); + if (!isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + var results = []; + for (var property in object) + results.push(property); + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) == "[object Array]"; + } + + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return typeof object === "function"; + } + + function isString(object) { + return _toString.call(object) == "[object String]"; + } + + function isNumber(object) { + return _toString.call(object) == "[object Number]"; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = slice.call(arguments, 1); + return function() { + var a = merge(args, arguments); + return __method.apply(context, a); + } + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000 + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + return { + argumentNames: argumentNames, + bind: bind, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + } +})()); + + +Date.prototype.toJSON = function() { + return '"' + this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z"'; +}; + + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; +var PeriodicalExecuter = Class.create({ + 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); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +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, (function() { + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || pattern.source)) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + 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; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + 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]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script) }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(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 (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + 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; + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function toJSON() { + return this.inspect(true); + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + var str = this; + if (str.blank()) return false; + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern) { + return this.indexOf(pattern) === 0; + } + + function endsWith(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim ? String.prototype.trim : strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + toJSON: toJSON, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: evalJSON, + include: include, + startsWith: startsWith, + endsWith: endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = (function() { + function each(iterator, context) { + var index = 0; + try { + this._each(function(value) { + iterator.call(context, value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(number, iterator, context) { + var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + } + + function all(iterator, context) { + iterator = iterator || Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator.call(context, value, index); + if (!result) throw $break; + }); + return result; + } + + function any(iterator, context) { + iterator = iterator || Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator.call(context, value, index)) + throw $break; + }); + return result; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index)); + }); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index)) { + result = value; + throw $break; + } + }); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index)) + results.push(value); + }); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index)); + }); + return results; + } + + function include(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index); + }); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index); + if (result == null || value >= result) + result = value; + }); + return result; + } + + function min(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index); + if (result == null || value < result) + result = value; + }); + return result; + } + + function partition(iterator, context) { + iterator = iterator || Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator.call(context, value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index)) + results.push(value); + }); + return results; + } + + function sortBy(iterator, context) { + return this.map(function(value, index) { + return { + value: value, + criteria: iterator.call(context, value, index) + }; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + } + + function toArray() { + return this.map(); + } + + function zip() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function toJSON() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } + + function indexOf(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; + } + + function lastIndexOf(item, i) { + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; + var n = this.slice(0, i).reverse().indexOf(item); + return (n < 0) ? n : i - n - 1; + } + + function concat() { + var array = slice.call(this, 0), item; + for (var i = 0, length = arguments.length; i < length; i++) { + item = arguments[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) + array.push(item[j]); + } else { + array.push(item); + } + } + return array; + } + + Object.extend(arrayProto, Enumerable); + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect, + toJSON: toJSON + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2) + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + function _each(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + function toQueryString() { + return this.inject([], function(results, pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) + return results.concat(values.map(toQueryPair.curry(key))); + } else results.push(toQueryPair(key, values)); + return results; + }).join('&'); + } + + function inspect() { + return '#'; + } + + function toJSON() { + return Object.toJSON(this.toObject()); + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toJSON, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function toJSON() { + return isFinite(this) ? this.toString() : 'null'; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + toJSON: toJSON, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +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 (Object.isFunction(responder[callback])) { + 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 = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isString(this.options.parameters)) + this.options.parameters = this.options.parameters.toQueryParams(); + else if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + 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)) { + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Object.toQueryString(params)) { + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + 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'; + } + + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + 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() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300); + }, + + getStatus: function() { + try { + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || 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.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + + + + + + + + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + json = decodeURIComponent(escape(json)); + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(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(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + 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 (Object.isString(element)) + 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(Element.extend(query.snapshotItem(i))); + return results; + }; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Node) var Node = { }; + +if (!Node.ELEMENT_NODE) { + Object.extend(Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); +} + + +(function(global) { + + var SETATTRIBUTE_IGNORES_NAME = (function(){ + var elForm = document.createElement("form"); + var elInput = document.createElement("input"); + var root = document.documentElement; + elInput.setAttribute("name", "test"); + elForm.appendChild(elInput); + root.appendChild(elForm); + var isBuggy = elForm.elements + ? (typeof elForm.elements.test == "undefined") + : null; + root.removeChild(elForm); + elForm = elInput = null; + return isBuggy; + })(); + + var element = global.Element; + global.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (SETATTRIBUTE_IGNORES_NAME && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + }; + Object.extend(global.Element, element || { }); + if (element) global.Element.prototype = element.prototype; +})(this); + +Element.cache = { }; +Element.idCounter = 1; + +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 = $(element); + element.style.display = 'none'; + return element; + }, + + show: function(element) { + element = $(element); + element.style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: (function(){ + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(element, content) { + element = $(element); + + if (content && content.toElement) + content = content.toElement(); + + if (Object.isElement(content)) + return element.update().insert(content); + + content = Object.toHTML(content); + + var tagName = element.tagName.toUpperCase(); + + if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { + if (tagName in Element._insertionTranslations.tags) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { + element.appendChild(node) + }); + } + else { + element.innerHTML = content.stripScripts(); + } + } + else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + return update; + })(), + + replace: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + element.parentNode.replaceChild(content, element); + return element; + }, + + insert: function(element, insertions) { + element = $(element); + + if (Object.isString(insertions) || Object.isNumber(insertions) || + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) + insertions = {bottom:insertions}; + + var content, insert, tagName, childNodes; + + for (var position in insertions) { + content = insertions[position]; + position = position.toLowerCase(); + insert = Element._insertionTranslations[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + insert(element, content); + continue; + } + + content = Object.toHTML(content); + + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); + + content.evalScripts.bind(content).defer(); + } + + return element; + }, + + wrap: function(element, wrapper, attributes) { + element = $(element); + if (Object.isElement(wrapper)) + $(wrapper).writeAttribute(attributes || { }); + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); + else wrapper = new Element('div', wrapper); + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + wrapper.appendChild(element); + return wrapper; + }, + + 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(element, 'parentNode'); + }, + + descendants: function(element) { + return Element.select(element, "*"); + }, + + 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(element, 'previousSibling'); + }, + + nextSiblings: function(element) { + return Element.recursivelyCollect(element, 'nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return Element.previousSiblings(element).reverse() + .concat(Element.nextSiblings(element)); + }, + + match: function(element, selector) { + if (Object.isString(selector)) + 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(element); + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return Element.firstDescendant(element); + return Object.isNumber(expression) ? Element.descendants(element)[expression] : + Element.select(element, expression)[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = Element.previousSiblings(element); + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = Element.nextSiblings(element); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + + select: function(element) { + var args = Array.prototype.slice.call(arguments, 1); + return Selector.findChildElements(element, args); + }, + + adjacent: function(element) { + var args = Array.prototype.slice.call(arguments, 1); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id)); + Element.writeAttribute(element, 'id', id); + return id; + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + var t = Element._attributeTranslations.read; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + if (name.include(':')) { + return (!element.attributes || !element.attributes[name]) ? null : + element.attributes[name].value; + } + } + return element.getAttribute(name); + }, + + writeAttribute: function(element, name, value) { + element = $(element); + var attributes = { }, t = Element._attributeTranslations.write; + + if (typeof name == 'object') attributes = name; + else attributes[name] = Object.isUndefined(value) ? true : value; + + for (var attr in attributes) { + name = t.names[attr] || attr; + value = attributes[attr]; + if (t.values[attr]) name = t.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + return element; + }, + + getHeight: function(element) { + return Element.getDimensions(element).height; + }, + + getWidth: function(element) { + return Element.getDimensions(element).width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + return (elementClassName.length > 0 && (elementClassName == className || + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + if (!Element.hasClassName(element, className)) + element.className += (element.className ? ' ' : '') + className; + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + element.className = element.className.replace( + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + return Element[Element.hasClassName(element, className) ? + 'removeClassName' : 'addClassName'](element, className); + }, + + 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); + + if (element.compareDocumentPosition) + return (element.compareDocumentPosition(ancestor) & 8) === 8; + + if (ancestor.contains) + return ancestor.contains(element) && ancestor !== element; + + while (element = element.parentNode) + if (element == ancestor) return true; + + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = Element.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 || value == 'auto') { + 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) { + element = $(element); + var elementStyle = element.style, match; + if (Object.isString(styles)) { + element.style.cssText += ';' + styles; + return styles.include('opacity') ? + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; + } + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]); + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : + property] = 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(element, 'display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari + 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'; + if (Prototype.Browser.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.getStyle(element, 'overflow') || 'auto'; + if (element._overflow !== '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; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return Element._returnOffset(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.toUpperCase() == 'BODY') break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + absolutize: function(element) { + element = $(element); + if (Element.getStyle(element, 'position') == 'absolute') return element; + + var offsets = Element.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'; + return element; + }, + + relativize: function(element) { + element = $(element); + if (Element.getStyle(element, 'position') == 'relative') return element; + + 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; + return element; + }, + + cumulativeScrollOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + getOffsetParent: 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); + }, + + viewportOffset: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + if (element.offsetParent == document.body && + Element.getStyle(element, 'position') == 'absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return Element._returnOffset(valueL, valueT); + }, + + clonePosition: function(element, source) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || { }); + + source = $(source); + var p = Element.viewportOffset(source); + + element = $(element); + var delta = [0, 0]; + var parent = null; + if (Element.getStyle(element, 'position') == 'absolute') { + parent = Element.getOffsetParent(element); + delta = Element.viewportOffset(parent); + } + + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if (options.setWidth) element.style.width = source.offsetWidth + 'px'; + if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + return element; + } +}; + +Object.extend(Element.Methods, { + getElementsBySelector: Element.Methods.select, + + childElements: Element.Methods.immediateDescendants +}); + +Element._attributeTranslations = { + write: { + names: { + className: 'class', + htmlFor: 'for' + }, + values: { } + } +}; + +if (Prototype.Browser.Opera) { + Element.Methods.getStyle = Element.Methods.getStyle.wrap( + function(proceed, element, style) { + switch (style) { + case 'left': case 'top': case 'right': case 'bottom': + if (proceed(element, 'position') === 'static') return null; + case 'height': case 'width': + if (!Element.visible(element)) return null; + + var dim = parseInt(proceed(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + var properties; + if (style === 'height') { + properties = ['border-top-width', 'padding-top', + 'padding-bottom', 'border-bottom-width']; + } + else { + properties = ['border-left-width', 'padding-left', + 'padding-right', 'border-right-width']; + } + return properties.inject(dim, function(memo, property) { + var val = proceed(element, property); + return val === null ? memo : memo - parseInt(val, 10); + }) + 'px'; + default: return proceed(element, style); + } + } + ); + + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( + function(proceed, element, attribute) { + if (attribute === 'title') return element.title; + return proceed(element, attribute); + } + ); +} + +else if (Prototype.Browser.IE) { + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + try { element.offsetParent } + catch(e) { return $(document.body) } + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { + Element.Methods[method] = Element.Methods[method].wrap( + function(proceed, element) { + element = $(element); + try { element.offsetParent } + catch(e) { return Element._returnOffset(0,0) } + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + }); + + Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( + function(proceed, element) { + try { element.offsetParent } + catch(e) { return Element._returnOffset(0,0) } + return proceed(element); + } + ); + + 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) { + function stripAlpha(filter){ + return filter.replace(/alpha\([^\)]*\)/gi,''); + } + element = $(element); + var currentStyle = element.currentStyle; + if ((currentStyle && !currentStyle.hasLayout) || + (!currentStyle && element.style.zoom == 'normal')) + element.style.zoom = 1; + + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + (filter = stripAlpha(filter)) ? + style.filter = filter : style.removeAttribute('filter'); + return element; + } else if (value < 0.00001) value = 0; + style.filter = stripAlpha(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + Element._attributeTranslations = (function(){ + + var classProp = 'className'; + var forProp = 'for'; + + var el = document.createElement('div'); + + el.setAttribute(classProp, 'x'); + + if (el.className !== 'x') { + el.setAttribute('class', 'x'); + if (el.className === 'x') { + classProp = 'class'; + } + } + el = null; + + el = document.createElement('label'); + el.setAttribute(forProp, 'x'); + if (el.htmlFor !== 'x') { + el.setAttribute('htmlFor', 'x'); + if (el.htmlFor === 'x') { + forProp = 'htmlFor'; + } + } + el = null; + + return { + read: { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute); + }, + _getAttr2: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: (function(){ + + var el = document.createElement('div'); + el.onclick = Prototype.emptyFunction; + var value = el.getAttribute('onclick'); + var f; + + if (String(value).indexOf('{') > -1) { + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + attribute = attribute.toString(); + attribute = attribute.split('{')[1]; + attribute = attribute.split('}')[0]; + return attribute.strip(); + }; + } + else if (value === '') { + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + return attribute.strip(); + }; + } + el = null; + return f; + })(), + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + } + } + })(); + + Element._attributeTranslations.write = { + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + Element._attributeTranslations.has = {}; + + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + + 'encType maxLength readOnly longDesc frameBorder').each(function(attr) { + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; + Element._attributeTranslations.has[attr.toLowerCase()] = attr; + }); + + (function(v) { + Object.extend(v, { + href: v._getAttr2, + src: v._getAttr2, + type: v._getAttr, + action: v._getAttrNode, + disabled: v._flag, + checked: v._flag, + readonly: v._flag, + multiple: v._flag, + onload: v._getEv, + onunload: v._getEv, + onclick: v._getEv, + ondblclick: v._getEv, + onmousedown: v._getEv, + onmouseup: v._getEv, + onmouseover: v._getEv, + onmousemove: v._getEv, + onmouseout: v._getEv, + onfocus: v._getEv, + onblur: v._getEv, + onkeypress: v._getEv, + onkeydown: v._getEv, + onkeyup: v._getEv, + onsubmit: v._getEv, + onreset: v._getEv, + onselect: v._getEv, + onchange: v._getEv + }); + })(Element._attributeTranslations.read.values); + + if (Prototype.BrowserFeatures.ElementExtensions) { + (function() { + function _descendants(element) { + var nodes = element.getElementsByTagName('*'), results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName !== "!") // Filter out comment nodes. + results.push(node); + return results; + } + + Element.Methods.down = function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? _descendants(element)[expression] : + Element.select(element, expression)[index || 0]; + } + })(); + } + +} + +else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +else if (Prototype.Browser.WebKit) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + + if (value == 1) + if(element.tagName.toUpperCase() == 'IMG' && element.width) { + element.width++; element.width--; + } else try { + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch (e) { } + + return element; + }; + + Element.Methods.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 Element._returnOffset(valueL, valueT); + }; +} + +if ('outerHTML' in document.documentElement) { + Element.Methods.replace = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (Element._insertionTranslations.tags[tagName]) { + var nextSibling = element.next(); + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + parent.removeChild(element); + if (nextSibling) + fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); + else + fragments.each(function(node) { parent.appendChild(node) }); + } + else element.outerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +Element._returnOffset = function(l, t) { + var result = [l, t]; + result.left = l; + result.top = t; + return result; +}; + +Element._getContentFromAnonymousElement = function(tagName, html) { + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; + return $A(div.childNodes); +}; + +Element._insertionTranslations = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + tags: { + TABLE: ['', '
        ', 1], + TBODY: ['', '
        ', 2], + TR: ['', '
        ', 3], + TD: ['
        ', '
        ', 4], + SELECT: ['', 1] + } +}; + +(function() { + var tags = Element._insertionTranslations.tags; + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); +})(); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + attribute = Element._attributeTranslations.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return !!(node && node.specified); + } +}; + +Element.Methods.ByTag = { }; + +Object.extend(Element, Element.Methods); + +(function(div) { + + if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) { + window.HTMLElement = { }; + window.HTMLElement.prototype = div['__proto__']; + Prototype.BrowserFeatures.ElementExtensions = true; + } + + div = null; + +})(document.createElement('div')) + +Element.extend = (function() { + + function checkDeficiency(tagName) { + if (typeof window.Element != 'undefined') { + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random()+'').slice(2); + var el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + } + return false; + } + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object'); + + if (Prototype.BrowserFeatures.SpecificElementExtensions) { + if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) { + return function(element) { + if (element && typeof element._extendedByPrototype == 'undefined') { + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + } + return element; + } + } + return Prototype.K; + } + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || typeof element._extendedByPrototype != 'undefined' || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + if (!Prototype.BrowserFeatures.ElementExtensions) { + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + } + } + }); + + extend.refresh(); + return extend; +})(); + +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 (Object.isArray(tagName)) 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; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + 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]; + + var element = document.createElement(tagName); + var proto = element['__proto__'] || element.constructor.prototype; + element = null; + return proto; + } + + var elementPrototype = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + copy(Element.Methods, elementPrototype); + copy(Element.Methods.Simulated, elementPrototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; + + if (Element.extend.refresh) Element.extend.refresh(); + Element.cache = { }; +}; + + +document.viewport = { + + getDimensions: function() { + return { width: this.getWidth(), height: this.getHeight() }; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; + +(function(viewport) { + var B = Prototype.Browser, doc = document, element, property = {}; + + function getRootElement() { + if (B.WebKit && !doc.evaluate) + return document; + + if (B.Opera && window.parseFloat(window.opera.version()) < 9.5) + return document.body; + + return document.documentElement; + } + + function define(D) { + if (!element) element = getRootElement(); + + property[D] = 'client' + D; + + viewport['get' + D] = function() { return element[property[D]] }; + return viewport['get' + D](); + } + + viewport.getWidth = define.curry('Width'); + + viewport.getHeight = define.curry('Height'); +})(document.viewport); + + +Element.Storage = { + UID: 1 +}; + +Element.addMethods({ + getStorage: function(element) { + if (!(element = $(element))) return; + + var uid; + if (element === window) { + uid = 0; + } else { + if (typeof element._prototypeUID === "undefined") + element._prototypeUID = [Element.Storage.UID++]; + uid = element._prototypeUID[0]; + } + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + }, + + store: function(element, key, value) { + if (!(element = $(element))) return; + + if (arguments.length === 2) { + Element.getStorage(element).update(key); + } else { + Element.getStorage(element).set(key, value); + } + + return element; + }, + + retrieve: function(element, key, defaultValue) { + if (!(element = $(element))) return; + var hash = Element.getStorage(element), value = hash.get(key); + + if (Object.isUndefined(value)) { + hash.set(key, defaultValue); + value = defaultValue; + } + + return value; + }, + + clone: function(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + clone._prototypeUID = void 0; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) { + descendants[i]._prototypeUID = void 0; + } + } + return Element.extend(clone); + } +}); +/* 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({ + initialize: function(expression) { + this.expression = expression.strip(); + + if (this.shouldUseSelectorsAPI()) { + this.mode = 'selectorsAPI'; + } else if (this.shouldUseXPath()) { + this.mode = 'xpath'; + this.compileXPathMatcher(); + } else { + this.mode = "normal"; + this.compileMatcher(); + } + + }, + + shouldUseXPath: (function() { + + var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ + var isBuggy = false; + if (document.evaluate && window.XPathResult) { + var el = document.createElement('div'); + el.innerHTML = '
        '; + + var xpath = ".//*[local-name()='ul' or local-name()='UL']" + + "//*[local-name()='li' or local-name()='LI']"; + + var result = document.evaluate(xpath, el, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + + isBuggy = (result.snapshotLength !== 2); + el = null; + } + return isBuggy; + })(); + + return function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + if ((/(\[[\w-]*?:|:checked)/).test(e)) + return false; + + if (IS_DESCENDANT_SELECTOR_BUGGY) return false; + + return true; + } + + })(), + + shouldUseSelectorsAPI: function() { + if (!Prototype.BrowserFeatures.SelectorsAPI) return false; + + if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; + + if (!Selector._div) Selector._div = new Element('div'); + + try { + Selector._div.querySelector(this.expression); + } catch(e) { + return false; + } + + return true; + }, + + compileMatcher: function() { + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m, len = ps.length, name; + + 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 = 0; i"; + } +}); + +if (Prototype.BrowserFeatures.SelectorsAPI && + document.compatMode === 'BackCompat') { + Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ + var div = document.createElement('div'), + span = document.createElement('span'); + + div.id = "prototype_test_id"; + span.className = 'Test'; + div.appendChild(span); + var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); + div = span = null; + return isIgnored; + })(); +} + +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: function(m) { + m[1] = m[1].toLowerCase(); + return new Template("[@#{1}]").evaluate(m); + }, + attr: function(m) { + m[1] = m[1].toLowerCase(); + 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 (Object.isFunction(h)) 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)]", + 'checked': "[@checked]", + 'disabled': "[(@disabled) and (@type!='hidden')]", + 'enabled': "[not(@disabled) and (@type!='hidden')]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, v, len = p.length, name; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i = 0; i= 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); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); 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: [ + { name: 'laterSibling', re: /^\s*~\s*/ }, + { name: 'child', re: /^\s*>\s*/ }, + { name: 'adjacent', re: /^\s*\+\s*/ }, + { name: 'descendant', re: /^\s/ }, + + { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, + { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, + { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, + { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, + { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, + { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } + ], + + assertions: { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }, + + handlers: { + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + mark: function(nodes) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: (function(){ + + var PROPERTIES_ATTRIBUTES_MAP = (function(){ + var el = document.createElement('div'), + isBuggy = false, + propName = '_countedByPrototype', + value = 'x' + el[propName] = value; + isBuggy = (el.getAttribute(propName) === value); + el = null; + return isBuggy; + })(); + + return PROPERTIES_ATTRIBUTES_MAP ? + function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } : + function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = void 0; + return nodes; + } + })(), + + index: function(parentNode, reverse, ofType) { + parentNode._countedByPrototype = Prototype.emptyFunction; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + var node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + }, + + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { + n._countedByPrototype = Prototype.emptyFunction; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + 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, 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; + }, + + tagName: function(nodes, root, tagName, combinator) { + var uTagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + 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() === uTagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + + if (root == document) { + if (!targetNode) return []; + if (!nodes) return [targetNode]; + } else { + if (!root.sourceIndex || root.sourceIndex < 1) { + var nodes = root.getElementsByTagName('*'); + for (var j = 0, node; node = nodes[j]; j++) { + if (node.id === id) return [node]; + } + } + } + + 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, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + 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, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + 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); + }, + + 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; + }); + }, + + 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._countedByPrototype) { + 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++) { + if (node.tagName == '!' || node.firstChild) 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._countedByPrototype) 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 && (!node.type || node.type !== 'hidden')) + 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 == v || nv && nv.startsWith(v); }, + '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, + '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + + '-').include('-' + (v || "").toUpperCase() + '-'); } + }, + + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + + matchElements: function(elements, expression) { + var matches = $$(expression), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._countedByPrototype) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (Object.isNumber(expression)) { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + expressions = Selector.split(expressions.join(',')); + 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; + } +}); + +if (Prototype.Browser.IE) { + Object.extend(Selector.handlers, { + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} + +var Form = { + reset: function(form) { + form = $(form); + form.reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit; + + var data = elements.inject({ }, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return options.hash ? data : Object.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + getElements: function(form) { + var elements = $(form).getElementsByTagName('*'), + element, + arr = [ ], + serializers = Form.Element.Serializers; + for (var i = 0; element = elements[i]; i++) { + arr.push(element); + } + return arr.inject([], function(elements, child) { + if (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) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return /^(?:input|select|textarea)$/i.test(element.tagName); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(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 Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return 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)$/i.test(element.type)))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + 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, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element, value); + default: + return Form.Element.Serializers.textarea(element, value); + } + }, + + inputSelector: function(element, value) { + if (Object.isUndefined(value)) return element.checked ? element.value : null; + else element.checked = !!value; + }, + + textarea: function(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + }, + + select: function(element, value) { + if (Object.isUndefined(value)) + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + else { + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; + } + } + else opt.selected = value.include(currentValue); + } + } + }, + + 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) { + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +}; + +/*--------------------------------------------------------------------------*/ + + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + 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, 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(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +(function() { + + var 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, + KEY_INSERT: 45, + + cache: {} + }; + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + var _isButton; + if (Prototype.Browser.IE) { + var buttonMap = { 0: 1, 1: 4, 2: 2 }; + _isButton = function(event, code) { + return event.button === buttonMap[code]; + }; + } else if (Prototype.Browser.WebKit) { + _isButton = function(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; + default: return false; + } + }; + } else { + _isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + + if (node.nodeType == Node.TEXT_NODE) + node = node.parentNode; + + return Element.extend(node); + } + + function findElement(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } + + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }); + + Event.extend = function(event, element) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + return Object.extend(event, methods); + }; + } else { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + Event.extend = Prototype.K; + } + + function _createResponder(element, eventName, handler) { + var registry = Element.retrieve(element, 'prototype_event_registry'); + + if (Object.isUndefined(registry)) { + CACHE.push(element); + registry = Element.retrieve(element, 'prototype_event_registry', $H()); + } + + var respondersForEvent = registry.get(eventName); + if (Object.isUndefined(respondersForEvent)) { + respondersForEvent = []; + registry.set(eventName, respondersForEvent); + } + + if (respondersForEvent.pluck('handler').include(handler)) return false; + + var responder; + if (eventName.include(":")) { + responder = function(event) { + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } else { + if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === "mouseenter" || eventName === "mouseleave")) { + if (eventName === "mouseenter" || eventName === "mouseleave") { + responder = function(event) { + Event.extend(event, element); + + var parent = event.relatedTarget; + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + + handler.call(element, event); + }; + } + } else { + responder = function(event) { + Event.extend(event, element); + handler.call(element, event); + }; + } + } + + responder.handler = handler; + respondersForEvent.push(responder); + return responder; + } + + function _destroyCache() { + for (var i = 0, length = CACHE.length; i < length; i++) { + Event.stopObserving(CACHE[i]); + CACHE[i] = null; + } + } + + var CACHE = []; + + if (Prototype.Browser.IE) + window.attachEvent('onunload', _destroyCache); + + if (Prototype.Browser.WebKit) + window.addEventListener('unload', Prototype.emptyFunction, false); + + + var _getDOMEventName = Prototype.K; + + if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { + _getDOMEventName = function(eventName) { + var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; + return eventName in translations ? translations[eventName] : eventName; + }; + } + + function observe(element, eventName, handler) { + element = $(element); + + var responder = _createResponder(element, eventName, handler); + + if (!responder) return element; + + if (eventName.include(':')) { + if (element.addEventListener) + element.addEventListener("dataavailable", responder, false); + else { + element.attachEvent("ondataavailable", responder); + element.attachEvent("onfilterchange", responder); + } + } else { + var actualEventName = _getDOMEventName(eventName); + + if (element.addEventListener) + element.addEventListener(actualEventName, responder, false); + else + element.attachEvent("on" + actualEventName, responder); + } + + return element; + } + + function stopObserving(element, eventName, handler) { + element = $(element); + + var registry = Element.retrieve(element, 'prototype_event_registry'); + + if (Object.isUndefined(registry)) return element; + + if (eventName && !handler) { + var responders = registry.get(eventName); + + if (Object.isUndefined(responders)) return element; + + responders.each( function(r) { + Element.stopObserving(element, eventName, r.handler); + }); + return element; + } else if (!eventName) { + registry.each( function(pair) { + var eventName = pair.key, responders = pair.value; + + responders.each( function(r) { + Element.stopObserving(element, eventName, r.handler); + }); + }); + return element; + } + + var responders = registry.get(eventName); + + if (!responders) return; + + var responder = responders.find( function(r) { return r.handler === handler; }); + if (!responder) return element; + + var actualEventName = _getDOMEventName(eventName); + + if (eventName.include(':')) { + if (element.removeEventListener) + element.removeEventListener("dataavailable", responder, false); + else { + element.detachEvent("ondataavailable", responder); + element.detachEvent("onfilterchange", responder); + } + } else { + if (element.removeEventListener) + element.removeEventListener(actualEventName, responder, false); + else + element.detachEvent('on' + actualEventName, responder); + } + + registry.set(eventName, responders.without(responder)); + + return element; + } + + function fire(element, eventName, memo, bubble) { + element = $(element); + + if (Object.isUndefined(bubble)) + bubble = true; + + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + + var event; + if (document.createEvent) { + event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', true, true); + } else { + event = document.createEventObject(); + event.eventType = bubble ? 'ondataavailable' : 'onfilterchange'; + } + + event.eventName = eventName; + event.memo = memo || { }; + + if (document.createEvent) + element.dispatchEvent(event); + else + element.fireEvent(event.eventType, event); + + return Event.extend(event); + } + + + Object.extend(Event, Event.Methods); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + loaded: false + }); + + if (window.Event) Object.extend(window.Event, Event); + else window.Event = Event; +})(); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearTimeout(timer); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.stopObserving('readystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { document.documentElement.doScroll('left'); } + catch(e) { + timer = pollDoScroll.defer(); + return; + } + fireContentLoadedEvent(); + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.observe('readystatechange', checkReadyState); + if (window == top) + timer = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(); + +Element.addMethods(); + +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.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 = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.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); + }, + + 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; + }, + + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +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); + +/*--------------------------------------------------------------------------*/ diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/html/robots.txt b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/robots.txt new file mode 100644 index 00000000000..085187fa58b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/html/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-Agent: * +# Disallow: / diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/code_statistics.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/code_statistics.rb new file mode 100644 index 00000000000..740d8a1767b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/code_statistics.rb @@ -0,0 +1,107 @@ +class CodeStatistics #:nodoc: + + TEST_TYPES = %w(Units Functionals Unit\ tests Functional\ tests Integration\ tests) + + def initialize(*pairs) + @pairs = pairs + @statistics = calculate_statistics + @total = calculate_total if pairs.length > 1 + end + + def to_s + print_header + @pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) } + print_splitter + + if @total + print_line("Total", @total) + print_splitter + end + + print_code_test_stats + end + + private + def calculate_statistics + @pairs.inject({}) { |stats, pair| stats[pair.first] = calculate_directory_statistics(pair.last); stats } + end + + def calculate_directory_statistics(directory, pattern = /.*\.rb$/) + stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 } + + Dir.foreach(directory) do |file_name| + if File.stat(directory + "/" + file_name).directory? and (/^\./ !~ file_name) + newstats = calculate_directory_statistics(directory + "/" + file_name, pattern) + stats.each { |k, v| stats[k] += newstats[k] } + end + + next unless file_name =~ pattern + + f = File.open(directory + "/" + file_name) + + while line = f.gets + stats["lines"] += 1 + stats["classes"] += 1 if line =~ /class [A-Z]/ + stats["methods"] += 1 if line =~ /def [a-z]/ + stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/ + end + end + + stats + end + + def calculate_total + total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 } + @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } } + total + end + + def calculate_code + code_loc = 0 + @statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k } + code_loc + end + + def calculate_tests + test_loc = 0 + @statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k } + test_loc + end + + def print_header + print_splitter + puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |" + print_splitter + end + + def print_splitter + puts "+----------------------+-------+-------+---------+---------+-----+-------+" + end + + def print_line(name, statistics) + m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0 + loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0 + + start = if TEST_TYPES.include? name + "| #{name.ljust(20)} " + else + "| #{name.ljust(20)} " + end + + puts start + + "| #{statistics["lines"].to_s.rjust(5)} " + + "| #{statistics["codelines"].to_s.rjust(5)} " + + "| #{statistics["classes"].to_s.rjust(7)} " + + "| #{statistics["methods"].to_s.rjust(7)} " + + "| #{m_over_c.to_s.rjust(3)} " + + "| #{loc_over_m.to_s.rjust(5)} |" + end + + def print_code_test_stats + code = calculate_code + tests = calculate_tests + + puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}" + puts "" + end + end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands.rb new file mode 100644 index 00000000000..841e98a0dc0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands.rb @@ -0,0 +1,17 @@ +commands = Dir["#{File.dirname(__FILE__)}/commands/*.rb"].collect { |file_path| File.basename(file_path).split(".").first } + +if commands.include?(ARGV.first) + require "#{File.dirname(__FILE__)}/commands/#{ARGV.shift}" +else + puts <<-USAGE +The 'run' provides a unified access point for all the default Rails' commands. + +Usage: ./script/run [OPTIONS] + +Examples: + ./script/run generate controller Admin + ./script/run process reaper + +USAGE + puts "Choose: #{commands.join(", ")}" +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/about.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/about.rb new file mode 100644 index 00000000000..bc2cfcb948c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/about.rb @@ -0,0 +1,3 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails/info' +puts Rails::Info diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/console.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/console.rb new file mode 100644 index 00000000000..63df8346396 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/console.rb @@ -0,0 +1,45 @@ +irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb' + +require 'optparse' + +options = { :sandbox => false, :irb => irb } +OptionParser.new do |opt| + opt.banner = "Usage: console [environment] [options]" + opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v } + opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v } + opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v } + opt.parse!(ARGV) +end + +libs = " -r irb/completion" +libs << %( -r "#{RAILS_ROOT}/config/environment") +libs << " -r console_app" +libs << " -r console_sandbox" if options[:sandbox] +libs << " -r console_with_helpers" + +if options[:debugger] + begin + require 'ruby-debug' + libs << " -r ruby-debug" + puts "=> Debugger enabled" + rescue Exception + puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'" + exit + end +end + +ENV['RAILS_ENV'] = case ARGV.first + when "p"; "production" + when "d"; "development" + when "t"; "test" + else + ARGV.first || ENV['RAILS_ENV'] || 'development' +end + +if options[:sandbox] + puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})" + puts "Any modifications you make will be rolled back on exit" +else + puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})" +end +exec "#{options[:irb]} #{libs} --simple-prompt" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/dbconsole.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/dbconsole.rb new file mode 100644 index 00000000000..e6f11a45db6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/dbconsole.rb @@ -0,0 +1,87 @@ +require 'erb' +require 'yaml' +require 'optparse' + +include_password = false +options = {} + +OptionParser.new do |opt| + opt.banner = "Usage: dbconsole [options] [environment]" + opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| + include_password = true + end + + opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], + "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| + options['mode'] = mode + end + + opt.on("-h", "--header") do |h| + options['header'] = h + end + + opt.parse!(ARGV) + abort opt.to_s unless (0..1).include?(ARGV.size) +end + +env = ARGV.first || ENV['RAILS_ENV'] || 'development' +unless config = YAML::load(ERB.new(IO.read(RAILS_ROOT + "/config/database.yml")).result)[env] + abort "No database is configured for the environment '#{env}'" +end + + +def find_cmd(*commands) + dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) + commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ + + full_path_command = nil + found = commands.detect do |cmd| + dir = dirs_on_path.detect do |path| + full_path_command = File.join(path, cmd) + File.executable? full_path_command + end + end + found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") +end + +case config["adapter"] +when "mysql" + args = { + 'host' => '--host', + 'port' => '--port', + 'socket' => '--socket', + 'username' => '--user', + 'encoding' => '--default-character-set' + }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact + + if config['password'] && include_password + args << "--password=#{config['password']}" + elsif config['password'] && !config['password'].to_s.empty? + args << "-p" + end + + args << config['database'] + + exec(find_cmd('mysql', 'mysql5'), *args) + +when "postgresql" + ENV['PGUSER'] = config["username"] if config["username"] + ENV['PGHOST'] = config["host"] if config["host"] + ENV['PGPORT'] = config["port"].to_s if config["port"] + ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password + exec(find_cmd('psql'), config["database"]) + +when "sqlite" + exec(find_cmd('sqlite'), config["database"]) + +when "sqlite3" + args = [] + + args << "-#{options['mode']}" if options['mode'] + args << "-header" if options['header'] + args << config['database'] + + exec(find_cmd('sqlite3'), *args) +else + abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/destroy.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/destroy.rb new file mode 100644 index 00000000000..f4b81d65117 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/destroy.rb @@ -0,0 +1,6 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails_generator' +require 'rails_generator/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +Rails::Generator::Scripts::Destroy.new.run(ARGV) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/generate.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/generate.rb new file mode 100644 index 00000000000..3d3db3d8565 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/generate.rb @@ -0,0 +1,6 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails_generator' +require 'rails_generator/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +Rails::Generator::Scripts::Generate.new.run(ARGV) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/ncgi/listener b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/ncgi/listener new file mode 100644 index 00000000000..7079ef78a6e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/ncgi/listener @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby + +require 'stringio' +require 'fileutils' +require 'fcgi_handler' + +def message(s) + $stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"] +end + +class RemoteCGI < CGI + attr_accessor :stdinput, :stdoutput, :env_table + def initialize(env_table, input = nil, output = nil) + self.env_table = env_table + self.stdinput = input || StringIO.new + self.stdoutput = output || StringIO.new + super() + end + + def out(stream) # Ignore the requested output stream + super(stdoutput) + end +end + +class Listener + include DRbUndumped + + def initialize(timeout, socket_path) + @socket = File.expand_path(socket_path) + @mutex = Mutex.new + @active = false + @timeout = timeout + + @handler = RailsFCGIHandler.new + @handler.extend DRbUndumped + + message 'opening socket' + DRb.start_service("drbunix:#{@socket}", self) + + message 'entering process loop' + @handler.process! self + end + + def each_cgi(&cgi_block) + @cgi_block = cgi_block + message 'entering idle loop' + loop do + sleep @timeout rescue nil + die! unless @active + @active = false + end + end + + def process(env, input) + message 'received request' + @mutex.synchronize do + @active = true + + message 'creating input stream' + input_stream = StringIO.new(input) + message 'building CGI instance' + cgi = RemoteCGI.new(eval(env), input_stream) + + message 'yielding to fcgi handler' + @cgi_block.call cgi + message 'yield finished -- sending output' + + cgi.stdoutput.seek(0) + output = cgi.stdoutput.read + + return output + end + end + + def die! + message 'shutting down' + DRb.stop_service + FileUtils.rm_f @socket + Kernel.exit 0 + end +end + +socket_path = ARGV.shift +timeout = (ARGV.shift || 90).to_i + +Listener.new(timeout, socket_path) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/ncgi/tracker b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/ncgi/tracker new file mode 100644 index 00000000000..4ca12d779b6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/ncgi/tracker @@ -0,0 +1,69 @@ +#!/usr/bin/env ruby + +require 'drb' +require 'thread' + +def message(s) + $stderr.puts "tracker: #{s}" if ENV && ENV["DEBUG_GATEWAY"] +end + +class Tracker + include DRbUndumped + + def initialize(instances, socket_path) + @instances = instances + @socket = File.expand_path(socket_path) + @active = false + + @listeners = [] + @instances.times { @listeners << Mutex.new } + + message "using #{@listeners.length} listeners" + message "opening socket at #{@socket}" + + @service = DRb.start_service("drbunix://#{@socket}", self) + end + + def with_listener + message "listener requested" + + mutex = has_lock = index = nil + 3.times do + @listeners.each_with_index do |mutex, index| + has_lock = mutex.try_lock + break if has_lock + end + break if has_lock + sleep 0.05 + end + + if has_lock + message "obtained listener #{index}" + @active = true + begin yield index + ensure + mutex.unlock + message "released listener #{index}" + end + else + message "dropping request because no listeners are available!" + end + end + + def background(check_interval = nil) + if check_interval + loop do + sleep check_interval + message "Idle for #{check_interval}, shutting down" unless @active + @active = false + Kernel.exit 0 + end + else DRb.thread.join + end + end +end + +socket_path = ARGV.shift +instances = ARGV.shift.to_i +t = Tracker.new(instances, socket_path) +t.background(ARGV.first ? ARGV.shift.to_i : 90) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/performance/benchmarker.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/performance/benchmarker.rb new file mode 100644 index 00000000000..e8804fe1bfd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/performance/benchmarker.rb @@ -0,0 +1,24 @@ +if ARGV.empty? + puts "Usage: ./script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..." + exit 1 +end + +begin + N = Integer(ARGV.first) + ARGV.shift +rescue ArgumentError + N = 1 +end + +require RAILS_ROOT + '/config/environment' +require 'benchmark' +include Benchmark + +# Don't include compilation in the benchmark +ARGV.each { |expression| eval(expression) } + +bm(6) do |x| + ARGV.each_with_index do |expression, idx| + x.report("##{idx + 1}") { N.times { eval(expression) } } + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/performance/profiler.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/performance/profiler.rb new file mode 100644 index 00000000000..7df840f1974 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/performance/profiler.rb @@ -0,0 +1,50 @@ +if ARGV.empty? + $stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]" + exit(1) +end + +# Keep the expensive require out of the profile. +$stderr.puts 'Loading Rails...' +require RAILS_ROOT + '/config/environment' + +# Define a method to profile. +if ARGV[1] and ARGV[1].to_i > 1 + eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end" +else + eval "def profile_me() #{ARGV[0]} end" +end + +# Use the ruby-prof extension if available. Fall back to stdlib profiler. +begin + begin + require "ruby-prof" + $stderr.puts 'Using the ruby-prof extension.' + RubyProf.measure_mode = RubyProf::WALL_TIME + RubyProf.start + profile_me + results = RubyProf.stop + if ARGV[2] + printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify) + else + printer_class = RubyProf::FlatPrinter + end + printer = printer_class.new(results) + printer.print($stderr) + rescue LoadError + require "prof" + $stderr.puts 'Using the old ruby-prof extension.' + Prof.clock_mode = Prof::GETTIMEOFDAY + Prof.start + profile_me + results = Prof.stop + require 'rubyprof_ext' + Prof.print_profile(results, $stderr) + end +rescue LoadError + require 'profiler' + $stderr.puts 'Using the standard Ruby profiler.' + Profiler__.start_profile + profile_me + Profiler__.stop_profile + Profiler__.print_profile($stderr) +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/plugin.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/plugin.rb new file mode 100644 index 00000000000..9f99a8d5575 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/plugin.rb @@ -0,0 +1,968 @@ +# Rails Plugin Manager. +# +# Listing available plugins: +# +# $ ./script/plugin list +# continuous_builder http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder +# asset_timestamping http://svn.aviditybytes.com/rails/plugins/asset_timestamping +# enumerations_mixin http://svn.protocool.com/rails/plugins/enumerations_mixin/trunk +# calculations http://techno-weenie.net/svn/projects/calculations/ +# ... +# +# Installing plugins: +# +# $ ./script/plugin install continuous_builder asset_timestamping +# +# Finding Repositories: +# +# $ ./script/plugin discover +# +# Adding Repositories: +# +# $ ./script/plugin source http://svn.protocool.com/rails/plugins/ +# +# How it works: +# +# * Maintains a list of subversion repositories that are assumed to have +# a plugin directory structure. Manage them with the (source, unsource, +# and sources commands) +# +# * The discover command scrapes the following page for things that +# look like subversion repositories with plugins: +# http://wiki.rubyonrails.org/rails/pages/Plugins +# +# * Unless you specify that you want to use svn, script/plugin uses plain old +# HTTP for downloads. The following bullets are true if you specify +# that you want to use svn. +# +# * If `vendor/plugins` is under subversion control, the script will +# modify the svn:externals property and perform an update. You can +# use normal subversion commands to keep the plugins up to date. +# +# * Or, if `vendor/plugins` is not under subversion control, the +# plugin is pulled via `svn checkout` or `svn export` but looks +# exactly the same. +# +# Specifying revisions: +# +# * Subversion revision is a single integer. +# +# * Git revision format: +# - full - 'refs/tags/1.8.0' or 'refs/heads/experimental' +# - short: 'experimental' (equivalent to 'refs/heads/experimental') +# 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0') +# +# +# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com) +# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php) + +$verbose = false + + +require 'open-uri' +require 'fileutils' +require 'tempfile' + +include FileUtils + +class RailsEnvironment + attr_reader :root + + def initialize(dir) + @root = dir + end + + def self.find(dir=nil) + dir ||= pwd + while dir.length > 1 + return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb')) + dir = File.dirname(dir) + end + end + + def self.default + @default ||= find + end + + def self.default=(rails_env) + @default = rails_env + end + + def install(name_uri_or_plugin) + if name_uri_or_plugin.is_a? String + if name_uri_or_plugin =~ /:\/\// + plugin = Plugin.new(name_uri_or_plugin) + else + plugin = Plugins[name_uri_or_plugin] + end + else + plugin = name_uri_or_plugin + end + unless plugin.nil? + plugin.install + else + puts "Plugin not found: #{name_uri_or_plugin}" + end + end + + def use_svn? + require 'active_support/core_ext/kernel' + silence_stderr {`svn --version` rescue nil} + !$?.nil? && $?.success? + end + + def use_externals? + use_svn? && File.directory?("#{root}/vendor/plugins/.svn") + end + + def use_checkout? + # this is a bit of a guess. we assume that if the rails environment + # is under subversion then they probably want the plugin checked out + # instead of exported. This can be overridden on the command line + File.directory?("#{root}/.svn") + end + + def best_install_method + return :http unless use_svn? + case + when use_externals? then :externals + when use_checkout? then :checkout + else :export + end + end + + def externals + return [] unless use_externals? + ext = `svn propget svn:externals "#{root}/vendor/plugins"` + lines = ext.respond_to?(:lines) ? ext.lines : ext + lines.reject{ |line| line.strip == '' }.map do |line| + line.strip.split(/\s+/, 2) + end + end + + def externals=(items) + unless items.is_a? String + items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n") + end + Tempfile.open("svn-set-prop") do |file| + file.write(items) + file.flush + system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"") + end + end + +end + +class Plugin + attr_reader :name, :uri + + def initialize(uri, name=nil) + @uri = uri + guess_name(uri) + end + + def self.find(name) + name =~ /\// ? new(name) : Repositories.instance.find_plugin(name) + end + + def to_s + "#{@name.ljust(30)}#{@uri}" + end + + def svn_url? + @uri =~ /svn(?:\+ssh)?:\/\/*/ + end + + def git_url? + @uri =~ /^git:\/\// || @uri =~ /\.git$/ + end + + def installed? + File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \ + or rails_env.externals.detect{ |name, repo| self.uri == repo } + end + + def install(method=nil, options = {}) + method ||= rails_env.best_install_method? + if :http == method + method = :export if svn_url? + method = :git if git_url? + end + + uninstall if installed? and options[:force] + + unless installed? + send("install_using_#{method}", options) + run_install_hook + else + puts "already installed: #{name} (#{uri}). pass --force to reinstall" + end + end + + def uninstall + path = "#{rails_env.root}/vendor/plugins/#{name}" + if File.directory?(path) + puts "Removing 'vendor/plugins/#{name}'" if $verbose + run_uninstall_hook + rm_r path + else + puts "Plugin doesn't exist: #{path}" + end + # clean up svn:externals + externals = rails_env.externals + externals.reject!{|n,u| name == n or name == u} + rails_env.externals = externals + end + + def info + tmp = "#{rails_env.root}/_tmp_about.yml" + if svn_url? + cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\"" + puts cmd if $verbose + system(cmd) + end + open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream| + stream.read + end rescue "No about.yml found in #{uri}" + ensure + FileUtils.rm_rf tmp if svn_url? + end + + private + + def run_install_hook + install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb" + load install_hook_file if File.exist? install_hook_file + end + + def run_uninstall_hook + uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb" + load uninstall_hook_file if File.exist? uninstall_hook_file + end + + def install_using_export(options = {}) + svn_command :export, options + end + + def install_using_checkout(options = {}) + svn_command :checkout, options + end + + def install_using_externals(options = {}) + externals = rails_env.externals + externals.push([@name, uri]) + rails_env.externals = externals + install_using_checkout(options) + end + + def install_using_http(options = {}) + root = rails_env.root + mkdir_p "#{root}/vendor/plugins/#{@name}" + Dir.chdir "#{root}/vendor/plugins/#{@name}" do + puts "fetching from '#{uri}'" if $verbose + fetcher = RecursiveHTTPFetcher.new(uri, -1) + fetcher.quiet = true if options[:quiet] + fetcher.fetch + end + end + + def install_using_git(options = {}) + root = rails_env.root + mkdir_p(install_path = "#{root}/vendor/plugins/#{name}") + Dir.chdir install_path do + init_cmd = "git init" + init_cmd += " -q" if options[:quiet] and not $verbose + puts init_cmd if $verbose + system(init_cmd) + base_cmd = "git pull --depth 1 #{uri}" + base_cmd += " -q" if options[:quiet] and not $verbose + base_cmd += " #{options[:revision]}" if options[:revision] + puts base_cmd if $verbose + if system(base_cmd) + puts "removing: .git .gitignore" if $verbose + rm_rf %w(.git .gitignore) + else + rm_rf install_path + end + end + end + + def svn_command(cmd, options = {}) + root = rails_env.root + mkdir_p "#{root}/vendor/plugins" + base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\"" + base_cmd += ' -q' if options[:quiet] and not $verbose + base_cmd += " -r #{options[:revision]}" if options[:revision] + puts base_cmd if $verbose + system(base_cmd) + end + + def guess_name(url) + @name = File.basename(url) + if @name == 'trunk' || @name.empty? + @name = File.basename(File.dirname(url)) + end + @name.gsub!(/\.git$/, '') if @name =~ /\.git$/ + end + + def rails_env + @rails_env || RailsEnvironment.default + end +end + +class Repositories + include Enumerable + + def initialize(cache_file = File.join(find_home, ".rails-plugin-sources")) + @cache_file = File.expand_path(cache_file) + load! + end + + def each(&block) + @repositories.each(&block) + end + + def add(uri) + unless find{|repo| repo.uri == uri } + @repositories.push(Repository.new(uri)).last + end + end + + def remove(uri) + @repositories.reject!{|repo| repo.uri == uri} + end + + def exist?(uri) + @repositories.detect{|repo| repo.uri == uri } + end + + def all + @repositories + end + + def find_plugin(name) + @repositories.each do |repo| + repo.each do |plugin| + return plugin if plugin.name == name + end + end + return nil + end + + def load! + contents = File.exist?(@cache_file) ? File.read(@cache_file) : defaults + contents = defaults if contents.empty? + @repositories = contents.split(/\n/).reject do |line| + line =~ /^\s*#/ or line =~ /^\s*$/ + end.map { |source| Repository.new(source.strip) } + end + + def save + File.open(@cache_file, 'w') do |f| + each do |repo| + f.write(repo.uri) + f.write("\n") + end + end + end + + def defaults + <<-DEFAULTS + http://dev.rubyonrails.com/svn/rails/plugins/ + DEFAULTS + end + + def find_home + ['HOME', 'USERPROFILE'].each do |homekey| + return ENV[homekey] if ENV[homekey] + end + if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] + return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" + end + begin + File.expand_path("~") + rescue StandardError => ex + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + + def self.instance + @instance ||= Repositories.new + end + + def self.each(&block) + self.instance.each(&block) + end +end + +class Repository + include Enumerable + attr_reader :uri, :plugins + + def initialize(uri) + @uri = uri.chomp('/') << "/" + @plugins = nil + end + + def plugins + unless @plugins + if $verbose + puts "Discovering plugins in #{@uri}" + puts index + end + + @plugins = index.reject{ |line| line !~ /\/$/ } + @plugins.map! { |name| Plugin.new(File.join(@uri, name), name) } + end + + @plugins + end + + def each(&block) + plugins.each(&block) + end + + private + def index + @index ||= RecursiveHTTPFetcher.new(@uri).ls + end +end + + +# load default environment and parse arguments +require 'optparse' +module Commands + + class Plugin + attr_reader :environment, :script_name, :sources + def initialize + @environment = RailsEnvironment.default + @rails_root = RailsEnvironment.default.root + @script_name = File.basename($0) + @sources = [] + end + + def environment=(value) + @environment = value + RailsEnvironment.default = value + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@script_name} [OPTIONS] command" + o.define_head "Rails plugin manager." + + o.separator "" + o.separator "GENERAL OPTIONS" + + o.on("-r", "--root=DIR", String, + "Set an explicit rails app directory.", + "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) } + o.on("-s", "--source=URL1,URL2", Array, + "Use the specified plugin repositories instead of the defaults.") { |sources| @sources = sources} + + o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose } + o.on("-h", "--help", "Show this help message.") { puts o; exit } + + o.separator "" + o.separator "COMMANDS" + + o.separator " discover Discover plugin repositories." + o.separator " list List available plugins." + o.separator " install Install plugin(s) from known repositories or URLs." + o.separator " update Update installed plugins." + o.separator " remove Uninstall plugins." + o.separator " source Add a plugin source repository." + o.separator " unsource Remove a plugin repository." + o.separator " sources List currently configured plugin repositories." + + o.separator "" + o.separator "EXAMPLES" + o.separator " Install a plugin:" + o.separator " #{@script_name} install continuous_builder\n" + o.separator " Install a plugin from a subversion URL:" + o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n" + o.separator " Install a plugin from a git URL:" + o.separator " #{@script_name} install git://github.com/SomeGuy/my_awesome_plugin.git\n" + o.separator " Install a plugin and add a svn:externals entry to vendor/plugins" + o.separator " #{@script_name} install -x continuous_builder\n" + o.separator " List all available plugins:" + o.separator " #{@script_name} list\n" + o.separator " List plugins in the specified repository:" + o.separator " #{@script_name} list --source=http://dev.rubyonrails.com/svn/rails/plugins/\n" + o.separator " Discover and prompt to add new repositories:" + o.separator " #{@script_name} discover\n" + o.separator " Discover new repositories but just list them, don't add anything:" + o.separator " #{@script_name} discover -l\n" + o.separator " Add a new repository to the source list:" + o.separator " #{@script_name} source http://dev.rubyonrails.com/svn/rails/plugins/\n" + o.separator " Remove a repository from the source list:" + o.separator " #{@script_name} unsource http://dev.rubyonrails.com/svn/rails/plugins/\n" + o.separator " Show currently configured repositories:" + o.separator " #{@script_name} sources\n" + end + end + + def parse!(args=ARGV) + general, sub = split_args(args) + options.parse!(general) + + command = general.shift + if command =~ /^(list|discover|install|source|unsource|sources|remove|update|info)$/ + command = Commands.const_get(command.capitalize).new(self) + command.parse!(sub) + else + puts "Unknown command: #{command}" + puts options + exit 1 + end + end + + def split_args(args) + left = [] + left << args.shift while args[0] and args[0] =~ /^-/ + left << args.shift if args[0] + return [left, args] + end + + def self.parse!(args=ARGV) + Plugin.new.parse!(args) + end + end + + + class List + def initialize(base_command) + @base_command = base_command + @sources = [] + @local = false + @remote = true + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} list [OPTIONS] [PATTERN]" + o.define_head "List available plugins." + o.separator "" + o.separator "Options:" + o.separator "" + o.on( "-s", "--source=URL1,URL2", Array, + "Use the specified plugin repositories.") {|sources| @sources = sources} + o.on( "--local", + "List locally installed plugins.") {|local| @local, @remote = local, false} + o.on( "--remote", + "List remotely available plugins. This is the default behavior", + "unless --local is provided.") {|remote| @remote = remote} + end + end + + def parse!(args) + options.order!(args) + unless @sources.empty? + @sources.map!{ |uri| Repository.new(uri) } + else + @sources = Repositories.instance.all + end + if @remote + @sources.map{|r| r.plugins}.flatten.each do |plugin| + if @local or !plugin.installed? + puts plugin.to_s + end + end + else + cd "#{@base_command.environment.root}/vendor/plugins" + Dir["*"].select{|p| File.directory?(p)}.each do |name| + puts name + end + end + end + end + + + class Sources + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} sources [OPTIONS] [PATTERN]" + o.define_head "List configured plugin repositories." + o.separator "" + o.separator "Options:" + o.separator "" + o.on( "-c", "--check", + "Report status of repository.") { |sources| @sources = sources} + end + end + + def parse!(args) + options.parse!(args) + Repositories.each do |repo| + puts repo.uri + end + end + end + + + class Source + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} source REPOSITORY [REPOSITORY [REPOSITORY]...]" + o.define_head "Add new repositories to the default search list." + end + end + + def parse!(args) + options.parse!(args) + count = 0 + args.each do |uri| + if Repositories.instance.add(uri) + puts "added: #{uri.ljust(50)}" if $verbose + count += 1 + else + puts "failed: #{uri.ljust(50)}" + end + end + Repositories.instance.save + puts "Added #{count} repositories." + end + end + + + class Unsource + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} unsource URI [URI [URI]...]" + o.define_head "Remove repositories from the default search list." + o.separator "" + o.on_tail("-h", "--help", "Show this help message.") { puts o; exit } + end + end + + def parse!(args) + options.parse!(args) + count = 0 + args.each do |uri| + if Repositories.instance.remove(uri) + count += 1 + puts "removed: #{uri.ljust(50)}" + else + puts "failed: #{uri.ljust(50)}" + end + end + Repositories.instance.save + puts "Removed #{count} repositories." + end + end + + + class Discover + def initialize(base_command) + @base_command = base_command + @list = false + @prompt = true + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} discover URI [URI [URI]...]" + o.define_head "Discover repositories referenced on a page." + o.separator "" + o.separator "Options:" + o.separator "" + o.on( "-l", "--list", + "List but don't prompt or add discovered repositories.") { |list| @list, @prompt = list, !@list } + o.on( "-n", "--no-prompt", + "Add all new repositories without prompting.") { |v| @prompt = !v } + end + end + + def parse!(args) + options.parse!(args) + args = ['http://wiki.rubyonrails.org/rails/pages/Plugins'] if args.empty? + args.each do |uri| + scrape(uri) do |repo_uri| + catch(:next_uri) do + if @prompt + begin + $stdout.print "Add #{repo_uri}? [Y/n] " + throw :next_uri if $stdin.gets !~ /^y?$/i + rescue Interrupt + $stdout.puts + exit 1 + end + elsif @list + puts repo_uri + throw :next_uri + end + Repositories.instance.add(repo_uri) + puts "discovered: #{repo_uri}" if $verbose or !@prompt + end + end + end + Repositories.instance.save + end + + def scrape(uri) + require 'open-uri' + puts "Scraping #{uri}" if $verbose + dupes = [] + content = open(uri).each do |line| + begin + if line =~ /]*href=['"]([^'"]*)['"]/ || line =~ /(svn:\/\/[^<|\n]*)/ + uri = $1 + if uri =~ /^\w+:\/\// && uri =~ /\/plugins\// && uri !~ /\/browser\// && uri !~ /^http:\/\/wiki\.rubyonrails/ && uri !~ /http:\/\/instiki/ + uri = extract_repository_uri(uri) + yield uri unless dupes.include?(uri) || Repositories.instance.exist?(uri) + dupes << uri + end + end + rescue + puts "Problems scraping '#{uri}': #{$!.to_s}" + end + end + end + + def extract_repository_uri(uri) + uri.match(/(svn|https?):.*\/plugins\//i)[0] + end + end + + class Install + def initialize(base_command) + @base_command = base_command + @method = :http + @options = { :quiet => false, :revision => nil, :force => false } + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]" + o.define_head "Install one or more plugins." + o.separator "" + o.separator "Options:" + o.on( "-x", "--externals", + "Use svn:externals to grab the plugin.", + "Enables plugin updates and plugin versioning.") { |v| @method = :externals } + o.on( "-o", "--checkout", + "Use svn checkout to grab the plugin.", + "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout } + o.on( "-e", "--export", + "Use svn export to grab the plugin.", + "Exports the plugin, allowing you to check it into your local repository. Does not enable updates, or add an svn:externals entry.") { |v| @method = :export } + o.on( "-q", "--quiet", + "Suppresses the output from installation.", + "Ignored if -v is passed (./script/plugin -v install ...)") { |v| @options[:quiet] = true } + o.on( "-r REVISION", "--revision REVISION", + "Checks out the given revision from subversion or git.", + "Ignored if subversion/git is not used.") { |v| @options[:revision] = v } + o.on( "-f", "--force", + "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true } + o.separator "" + o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to " + o.separator "a plugin repository." + end + end + + def determine_install_method + best = @base_command.environment.best_install_method + @method = :http if best == :http and @method == :export + case + when (best == :http and @method != :http) + msg = "Cannot install using subversion because `svn' cannot be found in your PATH" + when (best == :export and (@method != :export and @method != :http)) + msg = "Cannot install using #{@method} because this project is not under subversion." + when (best != :externals and @method == :externals) + msg = "Cannot install using externals because vendor/plugins is not under subversion." + end + if msg + puts msg + exit 1 + end + @method + end + + def parse!(args) + options.parse!(args) + environment = @base_command.environment + install_method = determine_install_method + puts "Plugins will be installed using #{install_method}" if $verbose + args.each do |name| + ::Plugin.find(name).install(install_method, @options) + end + rescue StandardError => e + puts "Plugin not found: #{args.inspect}" + puts e.inspect if $verbose + exit 1 + end + end + + class Update + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} update [name [name]...]" + o.on( "-r REVISION", "--revision REVISION", + "Checks out the given revision from subversion.", + "Ignored if subversion is not used.") { |v| @revision = v } + o.define_head "Update plugins." + end + end + + def parse!(args) + options.parse!(args) + root = @base_command.environment.root + cd root + args = Dir["vendor/plugins/*"].map do |f| + File.directory?("#{f}/.svn") ? File.basename(f) : nil + end.compact if args.empty? + cd "vendor/plugins" + args.each do |name| + if File.directory?(name) + puts "Updating plugin: #{name}" + system("svn #{$verbose ? '' : '-q'} up \"#{name}\" #{@revision ? "-r #{@revision}" : ''}") + else + puts "Plugin doesn't exist: #{name}" + end + end + end + end + + class Remove + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} remove name [name]..." + o.define_head "Remove plugins." + end + end + + def parse!(args) + options.parse!(args) + root = @base_command.environment.root + args.each do |name| + ::Plugin.new(name).uninstall + end + end + end + + class Info + def initialize(base_command) + @base_command = base_command + end + + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} info name [name]..." + o.define_head "Shows plugin info at {url}/about.yml." + end + end + + def parse!(args) + options.parse!(args) + args.each do |name| + puts ::Plugin.find(name).info + puts + end + end + end +end + +class RecursiveHTTPFetcher + attr_accessor :quiet + def initialize(urls_to_fetch, level = 1, cwd = ".") + @level = level + @cwd = cwd + @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a + @quiet = false + end + + def ls + @urls_to_fetch.collect do |url| + if url =~ /^svn(\+ssh)?:\/\/.*/ + `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil + else + open(url) do |stream| + links("", stream.read) + end rescue nil + end + end.flatten + end + + def push_d(dir) + @cwd = File.join(@cwd, dir) + FileUtils.mkdir_p(@cwd) + end + + def pop_d + @cwd = File.dirname(@cwd) + end + + def links(base_url, contents) + links = [] + contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link| + link = link.sub(/href="/i, "") + next if link =~ /svnindex.xsl$/ + next if link =~ /^(\w*:|)\/\// || link =~ /^\./ + links << File.join(base_url, link) + end + links + end + + def download(link) + puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet + open(link) do |stream| + File.open(File.join(@cwd, File.basename(link)), "wb") do |file| + file.write(stream.read) + end + end + end + + def fetch(links = @urls_to_fetch) + links.each do |l| + (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l) + end + end + + def fetch_dir(url) + @level += 1 + push_d(File.basename(url)) if @level > 0 + open(url) do |stream| + contents = stream.read + fetch(links(url, contents)) + end + pop_d if @level > 0 + @level -= 1 + end +end + +Commands::Plugin.parse! diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/runner.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/runner.rb new file mode 100644 index 00000000000..510128318a5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/runner.rb @@ -0,0 +1,54 @@ +require 'optparse' + +options = { :environment => (ENV['RAILS_ENV'] || "development").dup } +code_or_file = nil + +ARGV.clone.options do |opts| + script_name = File.basename($0) + opts.banner = "Usage: #{$0} [options] ('Some.ruby(code)' or a filename)" + + opts.separator "" + + opts.on("-e", "--environment=name", String, + "Specifies the environment for the runner to operate under (test/development/production).", + "Default: development") { |v| options[:environment] = v } + + opts.separator "" + + opts.on("-h", "--help", + "Show this help message.") { $stderr.puts opts; exit } + + if RUBY_PLATFORM !~ /mswin/ + opts.separator "" + opts.separator "You can also use runner as a shebang line for your scripts like this:" + opts.separator "-------------------------------------------------------------" + opts.separator "#!/usr/bin/env #{File.expand_path($0)}" + opts.separator "" + opts.separator "Product.find(:all).each { |p| p.price *= 2 ; p.save! }" + opts.separator "-------------------------------------------------------------" + end + + opts.order! { |o| code_or_file ||= o } rescue retry +end + +ARGV.delete(code_or_file) + +ENV["RAILS_ENV"] = options[:environment] +RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) + +require RAILS_ROOT + '/config/environment' + +begin + if code_or_file.nil? + $stderr.puts "Run '#{$0} -h' for help." + exit 1 + elsif File.exist?(code_or_file) + eval(File.read(code_or_file), nil, code_or_file) + else + eval(code_or_file) + end +ensure + if defined? Rails + Rails.logger.flush if Rails.logger.respond_to?(:flush) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/server.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/server.rb new file mode 100644 index 00000000000..b35c6871b1e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/server.rb @@ -0,0 +1,114 @@ +require 'active_support' +require 'action_controller' + +require 'fileutils' +require 'optparse' + +# TODO: Push Thin adapter upstream so we don't need worry about requiring it +begin + require_library_or_gem 'thin' +rescue Exception + # Thin not available +end + +options = { + :Port => 3000, + :Host => "127.0.0.1", + :environment => (ENV['RAILS_ENV'] || "development").dup, + :config => RAILS_ROOT + "/config.ru", + :detach => false, + :debugger => false, + :path => nil +} + +ARGV.clone.options do |opts| + opts.on("-p", "--port=port", Integer, + "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } + opts.on("-b", "--binding=ip", String, + "Binds Rails to the specified ip.", "Default: 0.0.0.0") { |v| options[:Host] = v } + opts.on("-c", "--config=file", String, + "Use custom rackup configuration file") { |v| options[:config] = v } + opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:detach] = true } + opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { options[:debugger] = true } + opts.on("-e", "--environment=name", String, + "Specifies the environment to run this server under (test/development/production).", + "Default: development") { |v| options[:environment] = v } + opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: /") { |v| options[:path] = v } + + opts.separator "" + + opts.on("-h", "--help", "Show this help message.") { puts opts; exit } + + opts.parse! +end + +server = Rack::Handler.get(ARGV.first) rescue nil +unless server + begin + server = Rack::Handler::Mongrel + rescue LoadError => e + server = Rack::Handler::WEBrick + end +end + +puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" +puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}#{options[:path]}" + +%w(cache pids sessions sockets).each do |dir_to_make| + FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) +end + +if options[:detach] + Process.daemon + pid = "#{RAILS_ROOT}/tmp/pids/server.pid" + File.open(pid, 'w'){ |f| f.write(Process.pid) } + at_exit { File.delete(pid) if File.exist?(pid) } +end + +ENV["RAILS_ENV"] = options[:environment] +RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) + +if File.exist?(options[:config]) + config = options[:config] + if config =~ /\.ru$/ + cfgfile = File.read(config) + if cfgfile[/^#\\(.*)/] + opts.parse!($1.split(/\s+/)) + end + inner_app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config) + else + require config + inner_app = Object.const_get(File.basename(config, '.rb').capitalize) + end +else + require RAILS_ROOT + "/config/environment" + inner_app = ActionController::Dispatcher.new +end + +if options[:path].nil? + map_path = "/" +else + ActionController::Base.relative_url_root = options[:path] + map_path = options[:path] +end + +app = Rack::Builder.new { + use Rails::Rack::LogTailer unless options[:detach] + use Rails::Rack::Debugger if options[:debugger] + map map_path do + use Rails::Rack::Static + run inner_app + end +}.to_app + +puts "=> Call with -d to detach" + +trap(:INT) { exit } + +puts "=> Ctrl-C to shutdown server" + +begin + server.run(app, options.merge(:AccessLog => [])) +ensure + puts 'Exiting' +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/update.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/update.rb new file mode 100644 index 00000000000..83ef833300a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/commands/update.rb @@ -0,0 +1,4 @@ +require "#{RAILS_ROOT}/config/environment" +require 'rails_generator' +require 'rails_generator/scripts/update' +Rails::Generator::Scripts::Update.new.run(ARGV) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_app.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_app.rb new file mode 100644 index 00000000000..d7d01d703fc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_app.rb @@ -0,0 +1,30 @@ +require 'active_support/test_case' +require 'action_controller' + +# work around the at_exit hook in test/unit, which kills IRB +Test::Unit.run = true if Test::Unit.respond_to?(:run=) + +# reference the global "app" instance, created on demand. To recreate the +# instance, pass a non-false value as the parameter. +def app(create=false) + @app_integration_instance = nil if create + @app_integration_instance ||= new_session do |sess| + sess.host! "www.example.com" + end +end + +# create a new session. If a block is given, the new session will be yielded +# to the block before being returned. +def new_session + session = ActionController::Integration::Session.new + yield session if block_given? + session +end + +#reloads the environment +def reload! + puts "Reloading..." + Dispatcher.cleanup_application + Dispatcher.reload_application + true +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_sandbox.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_sandbox.rb new file mode 100644 index 00000000000..65a3d686191 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_sandbox.rb @@ -0,0 +1,6 @@ +ActiveRecord::Base.connection.increment_open_transactions +ActiveRecord::Base.connection.begin_db_transaction +at_exit do + ActiveRecord::Base.connection.rollback_db_transaction + ActiveRecord::Base.connection.decrement_open_transactions +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_with_helpers.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_with_helpers.rb new file mode 100644 index 00000000000..039db667c40 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/console_with_helpers.rb @@ -0,0 +1,5 @@ +def helper + @helper ||= ApplicationController.helpers +end + +@controller = ApplicationController.new diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/dispatcher.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/dispatcher.rb new file mode 100644 index 00000000000..9f8b59aa3d2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/dispatcher.rb @@ -0,0 +1,24 @@ +#-- +# Copyright (c) 2004-2009 David Heinemeier Hansson +# +# 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. +#++ +require 'action_controller/dispatcher' +Dispatcher = ActionController::Dispatcher diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/fcgi_handler.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/fcgi_handler.rb new file mode 100644 index 00000000000..0cd2dc51c60 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/fcgi_handler.rb @@ -0,0 +1,239 @@ +require 'fcgi' +require 'logger' +require 'dispatcher' +require 'rbconfig' + +class RailsFCGIHandler + SIGNALS = { + 'HUP' => :reload, + 'INT' => :exit_now, + 'TERM' => :exit_now, + 'USR1' => :exit, + 'USR2' => :restart + } + GLOBAL_SIGNALS = SIGNALS.keys - %w(USR1) + + attr_reader :when_ready + + attr_accessor :log_file_path + attr_accessor :gc_request_period + + # Initialize and run the FastCGI instance, passing arguments through to new. + def self.process!(*args, &block) + new(*args, &block).process! + end + + # Initialize the FastCGI instance with the path to a crash log + # detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log) + # and the number of requests to process between garbage collection runs + # (default nil for normal GC behavior.) Optionally, pass a block which + # takes this instance as an argument for further configuration. + def initialize(log_file_path = nil, gc_request_period = nil) + self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log" + self.gc_request_period = gc_request_period + + # Yield for additional configuration. + yield self if block_given? + + # Safely install signal handlers. + install_signal_handlers + + @app = Dispatcher.new + + # Start error timestamp at 11 seconds ago. + @last_error_on = Time.now - 11 + end + + def process!(provider = FCGI) + mark_features! + + dispatcher_log :info, 'starting' + process_each_request provider + dispatcher_log :info, 'stopping gracefully' + + rescue Exception => error + case error + when SystemExit + dispatcher_log :info, 'stopping after explicit exit' + when SignalException + dispatcher_error error, 'stopping after unhandled signal' + else + # Retry if exceptions occur more than 10 seconds apart. + if Time.now - @last_error_on > 10 + @last_error_on = Time.now + dispatcher_error error, 'retrying after unhandled exception' + retry + else + dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last' + end + end + end + + protected + def process_each_request(provider) + request = nil + + catch :exit do + provider.each do |request| + process_request(request) + + case when_ready + when :reload + reload! + when :restart + close_connection(request) + restart! + when :exit + close_connection(request) + throw :exit + end + end + end + rescue SignalException => signal + raise unless signal.message == 'SIGUSR1' + close_connection(request) + end + + def process_request(request) + @processing, @when_ready = true, nil + gc_countdown + + with_signal_handler 'USR1' do + begin + ::Rack::Handler::FastCGI.serve(request, @app) + rescue SignalException, SystemExit + raise + rescue Exception => error + dispatcher_error error, 'unhandled dispatch error' + end + end + ensure + @processing = false + end + + def logger + @logger ||= Logger.new(@log_file_path) + end + + def dispatcher_log(level, msg) + time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S") + logger.send(level, "[#{time_str} :: #{$$}] #{msg}") + rescue Exception => log_error # Logger errors + STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n" + STDERR << " #{log_error.class}: #{log_error.message}\n" + end + + def dispatcher_error(e, msg = "") + error_message = + "Dispatcher failed to catch: #{e} (#{e.class})\n" + + " #{e.backtrace.join("\n ")}\n#{msg}" + dispatcher_log(:error, error_message) + end + + def install_signal_handlers + GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) } + end + + def install_signal_handler(signal, handler = nil) + if SIGNALS.include?(signal) && self.class.method_defined?(name = "#{SIGNALS[signal]}_handler") + handler ||= method(name).to_proc + + begin + trap(signal, handler) + rescue ArgumentError + dispatcher_log :warn, "Ignoring unsupported signal #{signal}." + end + else + dispatcher_log :warn, "Ignoring unsupported signal #{signal}." + end + end + + def with_signal_handler(signal) + install_signal_handler(signal) + yield + ensure + install_signal_handler(signal, 'DEFAULT') + end + + def exit_now_handler(signal) + dispatcher_log :info, "asked to stop immediately" + exit + end + + def exit_handler(signal) + dispatcher_log :info, "asked to stop ASAP" + if @processing + @when_ready = :exit + else + throw :exit + end + end + + def reload_handler(signal) + dispatcher_log :info, "asked to reload ASAP" + if @processing + @when_ready = :reload + else + reload! + end + end + + def restart_handler(signal) + dispatcher_log :info, "asked to restart ASAP" + if @processing + @when_ready = :restart + else + restart! + end + end + + def restart! + config = ::Config::CONFIG + ruby = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT'] + command_line = [ruby, $0, ARGV].flatten.join(' ') + + dispatcher_log :info, "restarted" + + # close resources as they won't be closed by + # the OS when using exec + logger.close rescue nil + Rails.logger.close rescue nil + + exec(command_line) + end + + def reload! + run_gc! if gc_request_period + restore! + @when_ready = nil + dispatcher_log :info, "reloaded" + end + + # Make a note of $" so we can safely reload this instance. + def mark_features! + @features = $".clone + end + + def restore! + $".replace @features + Dispatcher.reset_application! + ActionController::Routing::Routes.reload + end + + def run_gc! + @gc_request_countdown = gc_request_period + GC.enable; GC.start; GC.disable + end + + def gc_countdown + if gc_request_period + @gc_request_countdown ||= gc_request_period + @gc_request_countdown -= 1 + run_gc! if @gc_request_countdown <= 0 + end + end + + def close_connection(request) + request.finish if request + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/initializer.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/initializer.rb new file mode 100644 index 00000000000..196bd93b0c7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/initializer.rb @@ -0,0 +1,1128 @@ +require 'logger' +require 'set' +require 'pathname' + +$LOAD_PATH.unshift File.dirname(__FILE__) +require 'railties_path' +require 'rails/version' +require 'rails/plugin/locator' +require 'rails/plugin/loader' +require 'rails/gem_dependency' +require 'rails/rack' + + +RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV) + +module Rails + class << self + # The Configuration instance used to configure the Rails environment + def configuration + @@configuration + end + + def configuration=(configuration) + @@configuration = configuration + end + + def initialized? + @initialized || false + end + + def initialized=(initialized) + @initialized ||= initialized + end + + def logger + if defined?(RAILS_DEFAULT_LOGGER) + RAILS_DEFAULT_LOGGER + else + nil + end + end + + def backtrace_cleaner + @@backtrace_cleaner ||= begin + # Relies on ActiveSupport, so we have to lazy load to postpone definition until AS has been loaded + require 'rails/backtrace_cleaner' + Rails::BacktraceCleaner.new + end + end + + def root + Pathname.new(RAILS_ROOT) if defined?(RAILS_ROOT) + end + + def env + @_env ||= ActiveSupport::StringInquirer.new(RAILS_ENV) + end + + def cache + RAILS_CACHE + end + + def version + VERSION::STRING + end + + def public_path + @@public_path ||= self.root ? File.join(self.root, "public") : "public" + end + + def public_path=(path) + @@public_path = path + end + end + + # The Initializer is responsible for processing the Rails configuration, such + # as setting the $LOAD_PATH, requiring the right frameworks, initializing + # logging, and more. It can be run either as a single command that'll just + # use the default configuration, like this: + # + # Rails::Initializer.run + # + # But normally it's more interesting to pass in a custom configuration + # through the block running: + # + # Rails::Initializer.run do |config| + # config.frameworks -= [ :action_mailer ] + # end + # + # This will use the default configuration options from Rails::Configuration, + # but allow for overwriting on select areas. + class Initializer + # The Configuration instance used by this Initializer instance. + attr_reader :configuration + + # The set of loaded plugins. + attr_reader :loaded_plugins + + # Whether or not all the gem dependencies have been met + attr_reader :gems_dependencies_loaded + + # Runs the initializer. By default, this will invoke the #process method, + # which simply executes all of the initialization routines. Alternately, + # you can specify explicitly which initialization routine you want: + # + # Rails::Initializer.run(:set_load_path) + # + # This is useful if you only want the load path initialized, without + # incurring the overhead of completely loading the entire environment. + def self.run(command = :process, configuration = Configuration.new) + yield configuration if block_given? + initializer = new configuration + initializer.send(command) + initializer + end + + # Create a new Initializer instance that references the given Configuration + # instance. + def initialize(configuration) + @configuration = configuration + @loaded_plugins = [] + end + + # Sequentially step through all of the available initialization routines, + # in order (view execution order in source). + def process + Rails.configuration = configuration + + check_ruby_version + install_gem_spec_stubs + set_load_path + add_gem_load_paths + + require_frameworks + set_autoload_paths + add_plugin_load_paths + load_environment + preload_frameworks + + initialize_encoding + initialize_database + + initialize_cache + initialize_framework_caches + + initialize_logger + initialize_framework_logging + + initialize_dependency_mechanism + initialize_whiny_nils + + initialize_time_zone + initialize_i18n + + initialize_framework_settings + initialize_framework_views + + initialize_metal + + add_support_load_paths + + check_for_unbuilt_gems + + load_gems + load_plugins + + # pick up any gems that plugins depend on + add_gem_load_paths + load_gems + check_gem_dependencies + + # bail out if gems are missing - note that check_gem_dependencies will have + # already called abort() unless $gems_rake_task is set + return unless gems_dependencies_loaded + + load_application_initializers + + # the framework is now fully initialized + after_initialize + + # Setup database middleware after initializers have run + initialize_database_middleware + + # Prepare dispatcher callbacks and run 'prepare' callbacks + prepare_dispatcher + + # Routing must be initialized after plugins to allow the former to extend the routes + initialize_routing + + # Observers are loaded after plugins in case Observers or observed models are modified by plugins. + load_observers + + # Load view path cache + load_view_paths + + # Load application classes + load_application_classes + + # Disable dependency loading during request cycle + disable_dependency_loading + + # Flag initialized + Rails.initialized = true + end + + # Check for valid Ruby version + # This is done in an external file, so we can use it + # from the `rails` program as well without duplication. + def check_ruby_version + require 'ruby_version_check' + end + + # If Rails is vendored and RubyGems is available, install stub GemSpecs + # for Rails, Active Support, Active Record, Action Pack, Action Mailer, and + # Active Resource. This allows Gem plugins to depend on Rails even when + # the Gem version of Rails shouldn't be loaded. + def install_gem_spec_stubs + unless Rails.respond_to?(:vendor_rails?) + abort %{Your config/boot.rb is outdated: Run "rake rails:update".} + end + + if Rails.vendor_rails? + begin; require "rubygems"; rescue LoadError; return; end + + stubs = %w(rails activesupport activerecord actionpack actionmailer activeresource) + stubs.reject! { |s| Gem.loaded_specs.key?(s) } + + stubs.each do |stub| + Gem.loaded_specs[stub] = Gem::Specification.new do |s| + s.name = stub + s.version = Rails::VERSION::STRING + s.loaded_from = "" + end + end + end + end + + # Set the $LOAD_PATH based on the value of + # Configuration#load_paths. Duplicates are removed. + def set_load_path + load_paths = configuration.load_paths + configuration.framework_paths + load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) } + $LOAD_PATH.uniq! + end + + # Set the paths from which Rails will automatically load source files, and + # the load_once paths. + def set_autoload_paths + ActiveSupport::Dependencies.load_paths = configuration.load_paths.uniq + ActiveSupport::Dependencies.load_once_paths = configuration.load_once_paths.uniq + + extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths + unless extra.empty? + abort <<-end_error + load_once_paths must be a subset of the load_paths. + Extra items in load_once_paths: #{extra * ','} + end_error + end + + # Freeze the arrays so future modifications will fail rather than do nothing mysteriously + configuration.load_once_paths.freeze + end + + # Requires all frameworks specified by the Configuration#frameworks + # list. By default, all frameworks (Active Record, Active Support, + # Action Pack, Action Mailer, and Active Resource) are loaded. + def require_frameworks + configuration.frameworks.each { |framework| require(framework.to_s) } + rescue LoadError => e + # Re-raise as RuntimeError because Mongrel would swallow LoadError. + raise e.to_s + end + + # Preload all frameworks specified by the Configuration#frameworks. + # Used by Passenger to ensure everything's loaded before forking and + # to avoid autoload race conditions in JRuby. + def preload_frameworks + if configuration.preload_frameworks + configuration.frameworks.each do |framework| + # String#classify and #constantize aren't available yet. + toplevel = Object.const_get(framework.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }) + toplevel.load_all! if toplevel.respond_to?(:load_all!) + end + end + end + + # Add the load paths used by support functions such as the info controller + def add_support_load_paths + end + + # Adds all load paths from plugins to the global set of load paths, so that + # code from plugins can be required (explicitly or automatically via ActiveSupport::Dependencies). + def add_plugin_load_paths + plugin_loader.add_plugin_load_paths + end + + def add_gem_load_paths + Rails::GemDependency.add_frozen_gem_path + unless @configuration.gems.empty? + require "rubygems" + @configuration.gems.each { |gem| gem.add_load_paths } + end + end + + def load_gems + unless $gems_rake_task + @configuration.gems.each { |gem| gem.load } + end + end + + def check_for_unbuilt_gems + unbuilt_gems = @configuration.gems.select(&:frozen?).reject(&:built?) + if unbuilt_gems.size > 0 + # don't print if the gems:build rake tasks are being run + unless $gems_build_rake_task + abort <<-end_error +The following gems have native components that need to be built + #{unbuilt_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "} + +You're running: + ruby #{Gem.ruby_version} at #{Gem.ruby} + rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '} + +Run `rake gems:build` to build the unbuilt gems. + end_error + end + end + end + + def check_gem_dependencies + unloaded_gems = @configuration.gems.reject { |g| g.loaded? } + if unloaded_gems.size > 0 + @gems_dependencies_loaded = false + # don't print if the gems rake tasks are being run + unless $gems_rake_task + abort <<-end_error +Missing these required gems: + #{unloaded_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "} + +You're running: + ruby #{Gem.ruby_version} at #{Gem.ruby} + rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '} + +Run `rake gems:install` to install the missing gems. + end_error + end + else + @gems_dependencies_loaded = true + end + end + + # Loads all plugins in config.plugin_paths. plugin_paths + # defaults to vendor/plugins but may also be set to a list of + # paths, such as + # config.plugin_paths = ["#{RAILS_ROOT}/lib/plugins", "#{RAILS_ROOT}/vendor/plugins"] + # + # In the default implementation, as each plugin discovered in plugin_paths is initialized: + # * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory) + # * init.rb is evaluated, if present + # + # After all plugins are loaded, duplicates are removed from the load path. + # If an array of plugin names is specified in config.plugins, only those plugins will be loaded + # and they plugins will be loaded in that order. Otherwise, plugins are loaded in alphabetical + # order. + # + # if config.plugins ends contains :all then the named plugins will be loaded in the given order and all other + # plugins will be loaded in alphabetical order + def load_plugins + plugin_loader.load_plugins + end + + def plugin_loader + @plugin_loader ||= configuration.plugin_loader.new(self) + end + + # Loads the environment specified by Configuration#environment_path, which + # is typically one of development, test, or production. + def load_environment + silence_warnings do + return if @environment_loaded + @environment_loaded = true + + config = configuration + constants = self.class.constants + + eval(IO.read(configuration.environment_path), binding, configuration.environment_path) + + (self.class.constants - constants).each do |const| + Object.const_set(const, self.class.const_get(const)) + end + end + end + + def load_observers + if gems_dependencies_loaded && configuration.frameworks.include?(:active_record) + ActiveRecord::Base.instantiate_observers + end + end + + def load_view_paths + if configuration.frameworks.include?(:action_view) + ActionController::Base.view_paths.load! if configuration.frameworks.include?(:action_controller) + ActionMailer::Base.view_paths.load! if configuration.frameworks.include?(:action_mailer) + end + end + + # Eager load application classes + def load_application_classes + return if $rails_rake_task + if configuration.cache_classes + configuration.eager_load_paths.each do |load_path| + matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ + Dir.glob("#{load_path}/**/*.rb").sort.each do |file| + require_dependency file.sub(matcher, '\1') + end + end + end + end + + # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the + # multibyte safe operations. Plugin authors supporting other encodings + # should override this behaviour and set the relevant +default_charset+ + # on ActionController::Base. + # + # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby + # shebang line if you don't want UTF-8. + def initialize_encoding + $KCODE='u' if RUBY_VERSION < '1.9' + end + + # This initialization routine does nothing unless :active_record + # is one of the frameworks to load (Configuration#frameworks). If it is, + # this sets the database configuration from Configuration#database_configuration + # and then establishes the connection. + def initialize_database + if configuration.frameworks.include?(:active_record) + ActiveRecord::Base.configurations = configuration.database_configuration + ActiveRecord::Base.establish_connection + end + end + + def initialize_database_middleware + if configuration.frameworks.include?(:active_record) + if configuration.frameworks.include?(:action_controller) && + ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' + configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement + configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache + else + configuration.middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement + configuration.middleware.use ActiveRecord::QueryCache + end + end + end + + def initialize_cache + unless defined?(RAILS_CACHE) + silence_warnings { Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(configuration.cache_store) } + + if RAILS_CACHE.respond_to?(:middleware) + # Insert middleware to setup and teardown local cache for each request + configuration.middleware.insert_after(:"ActionController::Failsafe", RAILS_CACHE.middleware) + end + end + end + + def initialize_framework_caches + if configuration.frameworks.include?(:action_controller) + ActionController::Base.cache_store ||= RAILS_CACHE + end + end + + # If the RAILS_DEFAULT_LOGGER constant is already set, this initialization + # routine does nothing. If the constant is not set, and Configuration#logger + # is not +nil+, this also does nothing. Otherwise, a new logger instance + # is created at Configuration#log_path, with a default log level of + # Configuration#log_level. + # + # If the log could not be created, the log will be set to output to + # +STDERR+, with a log level of +WARN+. + def initialize_logger + # if the environment has explicitly defined a logger, use it + return if Rails.logger + + unless logger = configuration.logger + begin + logger = ActiveSupport::BufferedLogger.new(configuration.log_path) + logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase) + if configuration.environment == "production" + logger.auto_flushing = false + end + rescue StandardError => e + logger = ActiveSupport::BufferedLogger.new(STDERR) + logger.level = ActiveSupport::BufferedLogger::WARN + logger.warn( + "Rails Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " + + "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." + ) + end + end + + silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger } + end + + # Sets the logger for Active Record, Action Controller, and Action Mailer + # (but only for those frameworks that are to be loaded). If the framework's + # logger is already set, it is not changed, otherwise it is set to use + # RAILS_DEFAULT_LOGGER. + def initialize_framework_logging + for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks) + framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger + end + + ActiveSupport::Dependencies.logger ||= Rails.logger + Rails.cache.logger ||= Rails.logger + end + + # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ + # (but only for those frameworks that are to be loaded). If the framework's + # paths have already been set, it is not changed, otherwise it is + # set to use Configuration#view_path. + def initialize_framework_views + if configuration.frameworks.include?(:action_view) + view_path = ActionView::PathSet.type_cast(configuration.view_path) + ActionMailer::Base.template_root = view_path if configuration.frameworks.include?(:action_mailer) && ActionMailer::Base.view_paths.blank? + ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.blank? + end + end + + # If Action Controller is not one of the loaded frameworks (Configuration#frameworks) + # this does nothing. Otherwise, it loads the routing definitions and sets up + # loading module used to lazily load controllers (Configuration#controller_paths). + def initialize_routing + return unless configuration.frameworks.include?(:action_controller) + + ActionController::Routing.controller_paths += configuration.controller_paths + ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file) + ActionController::Routing::Routes.reload! + end + + # Sets the dependency loading mechanism based on the value of + # Configuration#cache_classes. + def initialize_dependency_mechanism + ActiveSupport::Dependencies.mechanism = configuration.cache_classes ? :require : :load + end + + # Loads support for "whiny nil" (noisy warnings when methods are invoked + # on +nil+ values) if Configuration#whiny_nils is true. + def initialize_whiny_nils + require('active_support/whiny_nil') if configuration.whiny_nils + end + + # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes. + # If assigned value cannot be matched to a TimeZone, an exception will be raised. + def initialize_time_zone + if configuration.time_zone + zone_default = Time.__send__(:get_zone, configuration.time_zone) + + unless zone_default + raise \ + 'Value assigned to config.time_zone not recognized.' + + 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' + end + + Time.zone_default = zone_default + + if configuration.frameworks.include?(:active_record) + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + end + end + end + + # Set the i18n configuration from config.i18n but special-case for the load_path which should be + # appended to what's already set instead of overwritten. + def initialize_i18n + configuration.i18n.each do |setting, value| + if setting == :load_path + I18n.load_path += value + else + I18n.send("#{setting}=", value) + end + end + end + + def initialize_metal + Rails::Rack::Metal.requested_metals = configuration.metals + Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths + + configuration.middleware.insert_before( + :"ActionController::ParamsParser", + Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?) + end + + # Initializes framework-specific settings for each of the loaded frameworks + # (Configuration#frameworks). The available settings map to the accessors + # on each of the corresponding Base classes. + def initialize_framework_settings + configuration.frameworks.each do |framework| + base_class = framework.to_s.camelize.constantize.const_get("Base") + + configuration.send(framework).each do |setting, value| + base_class.send("#{setting}=", value) + end + end + configuration.active_support.each do |setting, value| + ActiveSupport.send("#{setting}=", value) + end + end + + # Fires the user-supplied after_initialize block (Configuration#after_initialize) + def after_initialize + if gems_dependencies_loaded + configuration.after_initialize_blocks.each do |block| + block.call + end + end + end + + def load_application_initializers + if gems_dependencies_loaded + Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer| + load(initializer) + end + end + end + + def prepare_dispatcher + return unless configuration.frameworks.include?(:action_controller) + require 'dispatcher' unless defined?(::Dispatcher) + Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) + Dispatcher.run_prepare_callbacks + end + + def disable_dependency_loading + if configuration.cache_classes && !configuration.dependency_loading + ActiveSupport::Dependencies.unhook! + end + end + end + + # The Configuration class holds all the parameters for the Initializer and + # ships with defaults that suites most Rails applications. But it's possible + # to overwrite everything. Usually, you'll create an Configuration file + # implicitly through the block running on the Initializer, but it's also + # possible to create the Configuration instance in advance and pass it in + # like this: + # + # config = Rails::Configuration.new + # Rails::Initializer.run(:process, config) + class Configuration + # The application's base directory. + attr_reader :root_path + + # A stub for setting options on ActionController::Base. + attr_accessor :action_controller + + # A stub for setting options on ActionMailer::Base. + attr_accessor :action_mailer + + # A stub for setting options on ActionView::Base. + attr_accessor :action_view + + # A stub for setting options on ActiveRecord::Base. + attr_accessor :active_record + + # A stub for setting options on ActiveResource::Base. + attr_accessor :active_resource + + # A stub for setting options on ActiveSupport. + attr_accessor :active_support + + # Whether to preload all frameworks at startup. + attr_accessor :preload_frameworks + + # Whether or not classes should be cached (set to false if you want + # application classes to be reloaded on each request) + attr_accessor :cache_classes + + # The list of paths that should be searched for controllers. (Defaults + # to app/controllers.) + attr_accessor :controller_paths + + # The path to the database configuration file to use. (Defaults to + # config/database.yml.) + attr_accessor :database_configuration_file + + # The path to the routes configuration file to use. (Defaults to + # config/routes.rb.) + attr_accessor :routes_configuration_file + + # The list of rails framework components that should be loaded. (Defaults + # to :active_record, :action_controller, + # :action_view, :action_mailer, and + # :active_resource). + attr_accessor :frameworks + + # An array of additional paths to prepend to the load path. By default, + # all +app+, +lib+, +vendor+ and mock paths are included in this list. + attr_accessor :load_paths + + # An array of paths from which Rails will automatically load from only once. + # All elements of this array must also be in +load_paths+. + attr_accessor :load_once_paths + + # An array of paths from which Rails will eager load on boot if cache + # classes is enabled. All elements of this array must also be in + # +load_paths+. + attr_accessor :eager_load_paths + + # The log level to use for the default Rails logger. In production mode, + # this defaults to :info. In development mode, it defaults to + # :debug. + attr_accessor :log_level + + # The path to the log file to use. Defaults to log/#{environment}.log + # (e.g. log/development.log or log/production.log). + attr_accessor :log_path + + # The specific logger to use. By default, a logger will be created and + # initialized using #log_path and #log_level, but a programmer may + # specifically set the logger to use via this accessor and it will be + # used directly. + attr_accessor :logger + + # The specific cache store to use. By default, the ActiveSupport::Cache::Store will be used. + attr_accessor :cache_store + + # The root of the application's views. (Defaults to app/views.) + attr_accessor :view_path + + # Set to +true+ if you want to be warned (noisily) when you try to invoke + # any method of +nil+. Set to +false+ for the standard Ruby behavior. + attr_accessor :whiny_nils + + # The list of plugins to load. If this is set to nil, all plugins will + # be loaded. If this is set to [], no plugins will be loaded. Otherwise, + # plugins will be loaded in the order specified. + attr_reader :plugins + def plugins=(plugins) + @plugins = plugins.nil? ? nil : plugins.map { |p| p.to_sym } + end + + # The list of metals to load. If this is set to nil, all metals will + # be loaded in alphabetical order. If this is set to [], no metals will + # be loaded. Otherwise metals will be loaded in the order specified + attr_accessor :metals + + # The path to the root of the plugins directory. By default, it is in + # vendor/plugins. + attr_accessor :plugin_paths + + # The classes that handle finding the desired plugins that you'd like to load for + # your application. By default it is the Rails::Plugin::FileSystemLocator which finds + # plugins to load in vendor/plugins. You can hook into gem location by subclassing + # Rails::Plugin::Locator and adding it onto the list of plugin_locators. + attr_accessor :plugin_locators + + # The class that handles loading each plugin. Defaults to Rails::Plugin::Loader, but + # a sub class would have access to fine grained modification of the loading behavior. See + # the implementation of Rails::Plugin::Loader for more details. + attr_accessor :plugin_loader + + # Enables or disables plugin reloading. You can get around this setting per plugin. + # If reload_plugins? is false, add this to your plugin's init.rb + # to make it reloadable: + # + # ActiveSupport::Dependencies.load_once_paths.delete lib_path + # + # If reload_plugins? is true, add this to your plugin's init.rb + # to only load it once: + # + # ActiveSupport::Dependencies.load_once_paths << lib_path + # + attr_accessor :reload_plugins + + # Returns true if plugin reloading is enabled. + def reload_plugins? + !!@reload_plugins + end + + # Enables or disables dependency loading during the request cycle. Setting + # dependency_loading to true will allow new classes to be loaded + # during a request. Setting it to false will disable this behavior. + # + # Those who want to run in a threaded environment should disable this + # option and eager load or require all there classes on initialization. + # + # If cache_classes is disabled, dependency loaded will always be + # on. + attr_accessor :dependency_loading + + # An array of gems that this rails application depends on. Rails will automatically load + # these gems during installation, and allow you to install any missing gems with: + # + # rake gems:install + # + # You can add gems with the #gem method. + attr_accessor :gems + + # Adds a single Gem dependency to the rails application. By default, it will require + # the library with the same name as the gem. Use :lib to specify a different name. + # + # # gem 'aws-s3', '>= 0.4.0' + # # require 'aws/s3' + # config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \ + # :source => "http://code.whytheluckystiff.net" + # + # To require a library be installed, but not attempt to load it, pass :lib => false + # + # config.gem 'qrp', :version => '0.4.1', :lib => false + def gem(name, options = {}) + @gems << Rails::GemDependency.new(name, options) + end + + # Deprecated options: + def breakpoint_server(_ = nil) + $stderr.puts %( + ******************************************************************* + * config.breakpoint_server has been deprecated and has no effect. * + ******************************************************************* + ) + end + alias_method :breakpoint_server=, :breakpoint_server + + # Sets the default +time_zone+. Setting this will enable +time_zone+ + # awareness for Active Record models and set the Active Record default + # timezone to :utc. + attr_accessor :time_zone + + # Accessor for i18n settings. + attr_accessor :i18n + + # Create a new Configuration instance, initialized with the default + # values. + def initialize + set_root_path! + + self.frameworks = default_frameworks + self.load_paths = default_load_paths + self.load_once_paths = default_load_once_paths + self.eager_load_paths = default_eager_load_paths + self.log_path = default_log_path + self.log_level = default_log_level + self.view_path = default_view_path + self.controller_paths = default_controller_paths + self.preload_frameworks = default_preload_frameworks + self.cache_classes = default_cache_classes + self.dependency_loading = default_dependency_loading + self.whiny_nils = default_whiny_nils + self.plugins = default_plugins + self.plugin_paths = default_plugin_paths + self.plugin_locators = default_plugin_locators + self.plugin_loader = default_plugin_loader + self.database_configuration_file = default_database_configuration_file + self.routes_configuration_file = default_routes_configuration_file + self.gems = default_gems + self.i18n = default_i18n + + for framework in default_frameworks + self.send("#{framework}=", Rails::OrderedOptions.new) + end + self.active_support = Rails::OrderedOptions.new + end + + # Set the root_path to RAILS_ROOT and canonicalize it. + def set_root_path! + raise 'RAILS_ROOT is not set' unless defined?(::RAILS_ROOT) + raise 'RAILS_ROOT is not a directory' unless File.directory?(::RAILS_ROOT) + + @root_path = + # Pathname is incompatible with Windows, but Windows doesn't have + # real symlinks so File.expand_path is safe. + if RUBY_PLATFORM =~ /(:?mswin|mingw)/ + File.expand_path(::RAILS_ROOT) + + # Otherwise use Pathname#realpath which respects symlinks. + else + Pathname.new(::RAILS_ROOT).realpath.to_s + end + + Object.const_set(:RELATIVE_RAILS_ROOT, ::RAILS_ROOT.dup) unless defined?(::RELATIVE_RAILS_ROOT) + ::RAILS_ROOT.replace @root_path + end + + # Enable threaded mode. Allows concurrent requests to controller actions and + # multiple database connections. Also disables automatic dependency loading + # after boot, and disables reloading code on every request, as these are + # fundamentally incompatible with thread safety. + def threadsafe! + self.preload_frameworks = true + self.cache_classes = true + self.dependency_loading = false + self.action_controller.allow_concurrency = true + self + end + + # Loads and returns the contents of the #database_configuration_file. The + # contents of the file are processed via ERB before being sent through + # YAML::load. + def database_configuration + require 'erb' + YAML::load(ERB.new(IO.read(database_configuration_file)).result) + end + + # The path to the current environment's file (development.rb, etc.). By + # default the file is at config/environments/#{environment}.rb. + def environment_path + "#{root_path}/config/environments/#{environment}.rb" + end + + # Return the currently selected environment. By default, it returns the + # value of the RAILS_ENV constant. + def environment + ::RAILS_ENV + end + + # Adds a block which will be executed after rails has been fully initialized. + # Useful for per-environment configuration which depends on the framework being + # fully initialized. + def after_initialize(&after_initialize_block) + after_initialize_blocks << after_initialize_block if after_initialize_block + end + + # Returns the blocks added with Configuration#after_initialize + def after_initialize_blocks + @after_initialize_blocks ||= [] + end + + # Add a preparation callback that will run before every request in development + # mode, or before the first request in production. + # + # See Dispatcher#to_prepare. + def to_prepare(&callback) + after_initialize do + require 'dispatcher' unless defined?(::Dispatcher) + Dispatcher.to_prepare(&callback) + end + end + + def middleware + require 'action_controller' + ActionController::Dispatcher.middleware + end + + def builtin_directories + # Include builtins only in the development environment. + (environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : [] + end + + def framework_paths + paths = %w(railties railties/lib activesupport/lib) + paths << 'actionpack/lib' if frameworks.include?(:action_controller) || frameworks.include?(:action_view) + + [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework| + paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include?(framework) + end + + paths.map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + end + + private + def framework_root_path + defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root_path}/vendor/rails" + end + + def default_frameworks + [ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ] + end + + def default_load_paths + paths = [] + + # Add the old mock paths only if the directories exists + paths.concat(Dir["#{root_path}/test/mocks/#{environment}"]) if File.exists?("#{root_path}/test/mocks/#{environment}") + + # Add the app's controller directory + paths.concat(Dir["#{root_path}/app/controllers/"]) + + # Followed by the standard includes. + paths.concat %w( + app + app/metal + app/models + app/controllers + app/helpers + app/services + lib + vendor + ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + + paths.concat builtin_directories + end + + # Doesn't matter since plugins aren't in load_paths yet. + def default_load_once_paths + [] + end + + def default_eager_load_paths + %w( + app/metal + app/models + app/controllers + app/helpers + ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + end + + def default_log_path + File.join(root_path, 'log', "#{environment}.log") + end + + def default_log_level + environment == 'production' ? :info : :debug + end + + def default_database_configuration_file + File.join(root_path, 'config', 'database.yml') + end + + def default_routes_configuration_file + File.join(root_path, 'config', 'routes.rb') + end + + def default_view_path + File.join(root_path, 'app', 'views') + end + + def default_controller_paths + paths = [File.join(root_path, 'app', 'controllers')] + paths.concat builtin_directories + paths + end + + def default_dependency_loading + true + end + + def default_preload_frameworks + false + end + + def default_cache_classes + true + end + + def default_whiny_nils + false + end + + def default_plugins + nil + end + + def default_plugin_paths + ["#{root_path}/vendor/plugins"] + end + + def default_plugin_locators + locators = [] + locators << Plugin::GemLocator if defined? Gem + locators << Plugin::FileSystemLocator + end + + def default_plugin_loader + Plugin::Loader + end + + def default_cache_store + if File.exist?("#{root_path}/tmp/cache/") + [ :file_store, "#{root_path}/tmp/cache/" ] + else + :memory_store + end + end + + def default_gems + [] + end + + def default_i18n + i18n = Rails::OrderedOptions.new + i18n.load_path = [] + + if File.exist?(File.join(RAILS_ROOT, 'config', 'locales')) + i18n.load_path << Dir[File.join(RAILS_ROOT, 'config', 'locales', '*.{rb,yml}')] + i18n.load_path.flatten! + end + + i18n + end + end +end + +# Needs to be duplicated from Active Support since its needed before Active +# Support is available. Here both Options and Hash are namespaced to prevent +# conflicts with other implementations AND with the classes residing in Active Support. +class Rails::OrderedOptions < Array #:nodoc: + def []=(key, value) + key = key.to_sym + + if pair = find_pair(key) + pair.pop + pair << value + else + self << [key, value] + end + end + + def [](key) + pair = find_pair(key.to_sym) + pair ? pair.last : nil + end + + def method_missing(name, *args) + if name.to_s =~ /(.*)=$/ + self[$1.to_sym] = args.first + else + self[name] + end + end + + private + def find_pair(key) + self.each { |i| return i if i.first == key } + return false + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/performance_test_help.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/performance_test_help.rb new file mode 100644 index 00000000000..5148b4ab77c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/performance_test_help.rb @@ -0,0 +1,5 @@ +require 'action_controller/performance_test' + +ActionController::Base.perform_caching = true +ActiveSupport::Dependencies.mechanism = :require +Rails.logger.level = ActiveSupport::BufferedLogger::INFO diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/backtrace_cleaner.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/backtrace_cleaner.rb new file mode 100644 index 00000000000..923ed8b31dd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/backtrace_cleaner.rb @@ -0,0 +1,54 @@ +module Rails + class BacktraceCleaner < ActiveSupport::BacktraceCleaner + ERB_METHOD_SIG = /:in `_run_erb_.*/ + + RAILS_GEMS = %w( actionpack activerecord actionmailer activesupport activeresource rails ) + + VENDOR_DIRS = %w( vendor/rails ) + SERVER_DIRS = %w( lib/mongrel bin/mongrel + lib/passenger bin/passenger-spawn-server + lib/rack ) + RAILS_NOISE = %w( script/server ) + RUBY_NOISE = %w( rubygems/custom_require benchmark.rb ) + + ALL_NOISE = VENDOR_DIRS + SERVER_DIRS + RAILS_NOISE + RUBY_NOISE + + def initialize + super + add_filter { |line| line.sub("#{RAILS_ROOT}/", '') } + add_filter { |line| line.sub(ERB_METHOD_SIG, '') } + add_filter { |line| line.sub('./', '/') } # for tests + + add_gem_filters + + add_silencer { |line| ALL_NOISE.any? { |dir| line.include?(dir) } } + add_silencer { |line| RAILS_GEMS.any? { |gem| line =~ /^#{gem} / } } + add_silencer { |line| line =~ %r(vendor/plugins/[^\/]+/lib) } + end + + + private + def add_gem_filters + Gem.path.each do |path| + # http://gist.github.com/30430 + add_filter { |line| line.sub(/(#{path})\/gems\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) \4')} + end + + vendor_gems_path = Rails::GemDependency.unpacked_path.sub("#{RAILS_ROOT}/",'') + add_filter { |line| line.sub(/(#{vendor_gems_path})\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) [v] \4')} + end + end + + # For installing the BacktraceCleaner in the test/unit + module BacktraceFilterForTestUnit #:nodoc: + def self.included(klass) + klass.send :alias_method_chain, :filter_backtrace, :cleaning + end + + def filter_backtrace_with_cleaning(backtrace, prefix=nil) + backtrace = filter_backtrace_without_cleaning(backtrace, prefix) + backtrace = backtrace.first.split("\n") if backtrace.size == 1 + Rails.backtrace_cleaner.clean(backtrace) + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/gem_builder.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/gem_builder.rb new file mode 100644 index 00000000000..79c61cc0344 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/gem_builder.rb @@ -0,0 +1,21 @@ +require 'rubygems' +require 'rubygems/installer' + +module Rails + + # this class hijacks the functionality of Gem::Installer by overloading its + # initializer to only provide the information needed by + # Gem::Installer#build_extensions (which happens to be what we have) + class GemBuilder < Gem::Installer + + def initialize(spec, gem_dir) + @spec = spec + @gem_dir = gem_dir + end + + # silence the underlying builder + def say(message) + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/gem_dependency.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/gem_dependency.rb new file mode 100644 index 00000000000..06d830ba24a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/gem_dependency.rb @@ -0,0 +1,311 @@ +require 'rails/vendor_gem_source_index' + +module Gem + def self.source_index=(index) + @@source_index = index + end +end + +module Rails + class GemDependency < Gem::Dependency + attr_accessor :lib, :source, :dep + + def self.unpacked_path + @unpacked_path ||= File.join(RAILS_ROOT, 'vendor', 'gems') + end + + @@framework_gems = {} + + def self.add_frozen_gem_path + @@paths_loaded ||= begin + source_index = Rails::VendorGemSourceIndex.new(Gem.source_index) + Gem.clear_paths + Gem.source_index = source_index + # loaded before us - we can't change them, so mark them + Gem.loaded_specs.each do |name, spec| + @@framework_gems[name] = spec + end + true + end + end + + def self.from_directory_name(directory_name, load_spec=true) + directory_name_parts = File.basename(directory_name).split('-') + name = directory_name_parts[0..-2].join('-') + version = directory_name_parts.last + result = self.new(name, :version => version) + spec_filename = File.join(directory_name, '.specification') + if load_spec + raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'?" unless File.exists?(spec_filename) + spec = YAML::load_file(spec_filename) + result.specification = spec + end + result + rescue ArgumentError => e + raise "Unable to determine gem name and version from '#{directory_name}'" + end + + def initialize(name, options = {}) + require 'rubygems' unless Object.const_defined?(:Gem) + + if options[:requirement] + req = options[:requirement] + elsif options[:version] + req = Gem::Requirement.create(options[:version]) + else + req = Gem::Requirement.default + end + + @lib = options[:lib] + @source = options[:source] + @loaded = @frozen = @load_paths_added = false + + super(name, req) + end + + def add_load_paths + self.class.add_frozen_gem_path + return if @loaded || @load_paths_added + if framework_gem? + @load_paths_added = @loaded = @frozen = true + return + end + gem self + @spec = Gem.loaded_specs[name] + @frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec + @load_paths_added = true + rescue Gem::LoadError + end + + def dependencies + return [] if framework_gem? + return [] unless installed? + specification.dependencies.reject do |dependency| + dependency.type == :development + end.map do |dependency| + GemDependency.new(dependency.name, :requirement => dependency.version_requirements) + end + end + + def specification + # code repeated from Gem.activate. Find a matching spec, or the currently loaded version. + # error out if loaded version and requested version are incompatible. + @spec ||= begin + matches = Gem.source_index.search(self) + matches << @@framework_gems[name] if framework_gem? + if Gem.loaded_specs[name] then + # This gem is already loaded. If the currently loaded gem is not in the + # list of candidate gems, then we have a version conflict. + existing_spec = Gem.loaded_specs[name] + unless matches.any? { |spec| spec.version == existing_spec.version } then + raise Gem::Exception, + "can't activate #{@dep}, already activated #{existing_spec.full_name}" + end + # we're stuck with it, so change to match + version_requirements = Gem::Requirement.create("=#{existing_spec.version}") + existing_spec + else + # new load + matches.last + end + end + end + + def specification=(s) + @spec = s + end + + def requirement + r = version_requirements + (r == Gem::Requirement.default) ? nil : r + end + + def built? + return false unless frozen? + + if vendor_gem? + specification.extensions.each do |ext| + makefile = File.join(unpacked_gem_directory, File.dirname(ext), 'Makefile') + return false unless File.exists?(makefile) + end + end + + true + end + + def framework_gem? + @@framework_gems.has_key?(name) + end + + def frozen? + @frozen ||= vendor_rails? || vendor_gem? + end + + def installed? + Gem.loaded_specs.keys.include?(name) + end + + def load_paths_added? + # always try to add load paths - even if a gem is loaded, it may not + # be a compatible version (ie random_gem 0.4 is loaded and a later spec + # needs >= 0.5 - gem 'random_gem' will catch this and error out) + @load_paths_added + end + + def loaded? + @loaded ||= begin + if vendor_rails? + true + elsif specification.nil? + false + else + # check if the gem is loaded by inspecting $" + # specification.files lists all the files contained in the gem + gem_files = specification.files + # select only the files contained in require_paths - typically in bin and lib + require_paths_regexp = Regexp.new("^(#{specification.require_paths*'|'})/") + gem_lib_files = gem_files.select { |f| require_paths_regexp.match(f) } + # chop the leading directory off - a typical file might be in + # lib/gem_name/file_name.rb, but it will be 'require'd as gem_name/file_name.rb + gem_lib_files.map! { |f| f.split('/', 2)[1] } + # if any of the files from the above list appear in $", the gem is assumed to + # have been loaded + !(gem_lib_files & $").empty? + end + end + end + + def vendor_rails? + Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.empty? + end + + def vendor_gem? + specification && File.exists?(unpacked_gem_directory) + end + + def build(options={}) + require 'rails/gem_builder' + return if specification.nil? + if options[:force] || !built? + return unless File.exists?(unpacked_specification_filename) + spec = YAML::load_file(unpacked_specification_filename) + Rails::GemBuilder.new(spec, unpacked_gem_directory).build_extensions + puts "Built gem: '#{unpacked_gem_directory}'" + end + dependencies.each { |dep| dep.build(options) } + end + + def install + unless installed? + cmd = "#{gem_command} #{install_command.join(' ')}" + puts cmd + puts %x(#{cmd}) + end + end + + def load + return if @loaded || @load_paths_added == false + require(@lib || name) unless @lib == false + @loaded = true + rescue LoadError + puts $!.to_s + $!.backtrace.each { |b| puts b } + end + + def refresh + Rails::VendorGemSourceIndex.silence_spec_warnings = true + real_gems = Gem.source_index.installed_source_index + exact_dep = Gem::Dependency.new(name, "= #{specification.version}") + matches = real_gems.search(exact_dep) + installed_spec = matches.first + if frozen? + if installed_spec + # we have a real copy + # get a fresh spec - matches should only have one element + # note that there is no reliable method to check that the loaded + # spec is the same as the copy from real_gems - Gem.activate changes + # some of the fields + real_spec = Gem::Specification.load(matches.first.loaded_from) + write_specification(real_spec) + puts "Reloaded specification for #{name} from installed gems." + else + # the gem isn't installed locally - write out our current specs + write_specification(specification) + puts "Gem #{name} not loaded locally - writing out current spec." + end + else + if framework_gem? + puts "Gem directory for #{name} not found - check if it's loading before rails." + else + puts "Something bad is going on - gem directory not found for #{name}." + end + end + end + + def unpack(options={}) + unless frozen? || framework_gem? + FileUtils.mkdir_p unpack_base + Dir.chdir unpack_base do + Gem::GemRunner.new.run(unpack_command) + end + # Gem.activate changes the spec - get the original + real_spec = Gem::Specification.load(specification.loaded_from) + write_specification(real_spec) + end + dependencies.each { |dep| dep.unpack(options) } if options[:recursive] + end + + def write_specification(spec) + # copy the gem's specification into GEMDIR/.specification so that + # we can access information about the gem on deployment systems + # without having the gem installed + File.open(unpacked_specification_filename, 'w') do |file| + file.puts spec.to_yaml + end + end + + def ==(other) + self.name == other.name && self.requirement == other.requirement + end + alias_method :"eql?", :"==" + + private + + def gem_command + case RUBY_PLATFORM + when /win32/ + 'gem.bat' + when /java/ + 'jruby -S gem' + else + 'gem' + end + end + + def install_command + cmd = %w(install) << name + cmd << "--version" << %("#{requirement.to_s}") if requirement + cmd << "--source" << @source if @source + cmd + end + + def unpack_command + cmd = %w(unpack) << name + cmd << "--version" << "= "+specification.version.to_s if requirement + cmd + end + + def unpack_base + Rails::GemDependency.unpacked_path + end + + def unpacked_gem_directory + File.join(unpack_base, specification.full_name) + end + + def unpacked_specification_filename + File.join(unpacked_gem_directory, '.specification') + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin.rb new file mode 100644 index 00000000000..1dc4d142c5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin.rb @@ -0,0 +1,179 @@ +module Rails + # The Plugin class should be an object which provides the following methods: + # + # * +name+ - Used during initialisation to order the plugin (based on name and + # the contents of config.plugins). + # * +valid?+ - Returns true if this plugin can be loaded. + # * +load_paths+ - Each path within the returned array will be added to the $LOAD_PATH. + # * +load+ - Finally 'load' the plugin. + # + # These methods are expected by the Rails::Plugin::Locator and Rails::Plugin::Loader classes. + # The default implementation returns the lib directory as its load_paths, + # and evaluates init.rb when load is called. + # + # You can also inspect the about.yml data programmatically: + # + # plugin = Rails::Plugin.new(path_to_my_plugin) + # plugin.about["author"] # => "James Adam" + # plugin.about["url"] # => "http://interblah.net" + class Plugin + include Comparable + + attr_reader :directory, :name + + def initialize(directory) + @directory = directory + @name = File.basename(@directory) rescue nil + @loaded = false + end + + def valid? + File.directory?(directory) && (has_app_directory? || has_lib_directory? || has_init_file?) + end + + # Returns a list of paths this plugin wishes to make available in $LOAD_PATH. + def load_paths + report_nonexistant_or_empty_plugin! unless valid? + + returning [] do |load_paths| + load_paths << lib_path if has_lib_directory? + load_paths << app_paths if has_app_directory? + end.flatten + end + + # Evaluates a plugin's init.rb file. + def load(initializer) + return if loaded? + report_nonexistant_or_empty_plugin! unless valid? + evaluate_init_rb(initializer) + @loaded = true + end + + def loaded? + @loaded + end + + def <=>(other_plugin) + name <=> other_plugin.name + end + + def about + @about ||= load_about_information + end + + # Engines are plugins with an app/ directory. + def engine? + has_app_directory? + end + + # Returns true if the engine ships with a routing file + def routed? + File.exist?(routing_file) + end + + # Returns true if there is any localization file in locale_path + def localized? + locale_files.any? + end + + def view_path + File.join(directory, 'app', 'views') + end + + def controller_path + File.join(directory, 'app', 'controllers') + end + + def metal_path + File.join(directory, 'app', 'metal') + end + + def routing_file + File.join(directory, 'config', 'routes.rb') + end + + def locale_path + File.join(directory, 'config', 'locales') + end + + def locale_files + Dir[ File.join(locale_path, '*.{rb,yml}') ] + end + + + private + def load_about_information + about_yml_path = File.join(@directory, "about.yml") + parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {} + parsed_yml || {} + rescue Exception + {} + end + + def report_nonexistant_or_empty_plugin! + raise LoadError, "Can not find the plugin named: #{name}" + end + + + def app_paths + [ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path, metal_path ] + end + + def lib_path + File.join(directory, 'lib') + end + + def classic_init_path + File.join(directory, 'init.rb') + end + + def gem_init_path + File.join(directory, 'rails', 'init.rb') + end + + def init_path + File.file?(gem_init_path) ? gem_init_path : classic_init_path + end + + + def has_app_directory? + File.directory?(File.join(directory, 'app')) + end + + def has_lib_directory? + File.directory?(lib_path) + end + + def has_init_file? + File.file?(init_path) + end + + + def evaluate_init_rb(initializer) + if has_init_file? + silence_warnings do + # Allow plugins to reference the current configuration object + config = initializer.configuration + + eval(IO.read(init_path), binding, init_path) + end + end + end + end + + # This Plugin subclass represents a Gem plugin. Although RubyGems has already + # taken care of $LOAD_PATHs, it exposes its load_paths to add them + # to Dependencies.load_paths. + class GemPlugin < Plugin + # Initialize this plugin from a Gem::Specification. + def initialize(spec, gem) + directory = spec.full_gem_path + super(directory) + @name = spec.name + end + + def init_path + File.join(directory, 'rails', 'init.rb') + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin/loader.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin/loader.rb new file mode 100644 index 00000000000..49670b31e6b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin/loader.rb @@ -0,0 +1,198 @@ +require "rails/plugin" + +module Rails + class Plugin + class Loader + attr_reader :initializer + + # Creates a new Plugin::Loader instance, associated with the given + # Rails::Initializer. This default implementation automatically locates + # all plugins, and adds all plugin load paths, when it is created. The plugins + # are then fully loaded (init.rb is evaluated) when load_plugins is called. + # + # It is the loader's responsibility to ensure that only the plugins specified + # in the configuration are actually loaded, and that the order defined + # is respected. + def initialize(initializer) + @initializer = initializer + end + + # Returns the plugins to be loaded, in the order they should be loaded. + def plugins + @plugins ||= all_plugins.select { |plugin| should_load?(plugin) }.sort { |p1, p2| order_plugins(p1, p2) } + end + + # Returns the plugins that are in engine-form (have an app/ directory) + def engines + @engines ||= plugins.select(&:engine?) + end + + # Returns all the plugins that could be found by the current locators. + def all_plugins + @all_plugins ||= locate_plugins + @all_plugins + end + + def load_plugins + plugins.each do |plugin| + plugin.load(initializer) + register_plugin_as_loaded(plugin) + end + + configure_engines + + ensure_all_registered_plugins_are_loaded! + end + + # Adds the load paths for every plugin into the $LOAD_PATH. Plugin load paths are + # added *after* the application's lib directory, to ensure that an application + # can always override code within a plugin. + # + # Plugin load paths are also added to Dependencies.load_paths, and Dependencies.load_once_paths. + def add_plugin_load_paths + plugins.each do |plugin| + plugin.load_paths.each do |path| + $LOAD_PATH.insert(application_lib_index + 1, path) + + ActiveSupport::Dependencies.load_paths << path + + unless configuration.reload_plugins? + ActiveSupport::Dependencies.load_once_paths << path + end + end + end + + $LOAD_PATH.uniq! + end + + def engine_metal_paths + engines.collect(&:metal_path) + end + + protected + def configure_engines + if engines.any? + add_engine_routing_configurations + add_engine_locales + add_engine_controller_paths + add_engine_view_paths + end + end + + def add_engine_routing_configurations + engines.select(&:routed?).collect(&:routing_file).each do |routing_file| + ActionController::Routing::Routes.add_configuration_file(routing_file) + end + end + + def add_engine_locales + # reverse it such that the last engine can overwrite translations from the first, like with routes + locale_files = engines.select(&:localized?).collect(&:locale_files).reverse.flatten + I18n.load_path += locale_files - I18n.load_path + end + + def add_engine_controller_paths + ActionController::Routing.controller_paths += engines.collect(&:controller_path) + end + + def add_engine_view_paths + # reverse it such that the last engine can overwrite view paths from the first, like with routes + paths = ActionView::PathSet.new(engines.collect(&:view_path).reverse) + ActionController::Base.view_paths.concat(paths) + ActionMailer::Base.view_paths.concat(paths) if configuration.frameworks.include?(:action_mailer) + end + + # The locate_plugins method uses each class in config.plugin_locators to + # find the set of all plugins available to this Rails application. + def locate_plugins + configuration.plugin_locators.map do |locator| + locator.new(initializer).plugins + end.flatten + # TODO: sorting based on config.plugins + end + + def register_plugin_as_loaded(plugin) + initializer.loaded_plugins << plugin + end + + def configuration + initializer.configuration + end + + def should_load?(plugin) + # uses Plugin#name and Plugin#valid? + enabled?(plugin) && plugin.valid? + end + + def order_plugins(plugin_a, plugin_b) + if !explicit_plugin_loading_order? + plugin_a <=> plugin_b + else + if !explicitly_enabled?(plugin_a) && !explicitly_enabled?(plugin_b) + plugin_a <=> plugin_b + else + effective_order_of(plugin_a) <=> effective_order_of(plugin_b) + end + end + end + + def effective_order_of(plugin) + if explicitly_enabled?(plugin) + registered_plugin_names.index(plugin.name) + else + registered_plugin_names.index('all') + end + end + + def application_lib_index + $LOAD_PATH.index(File.join(RAILS_ROOT, 'lib')) || 0 + end + + def enabled?(plugin) + !explicit_plugin_loading_order? || registered?(plugin) + end + + def explicit_plugin_loading_order? + !registered_plugin_names.nil? + end + + def registered?(plugin) + explicit_plugin_loading_order? && registered_plugins_names_plugin?(plugin) + end + + def explicitly_enabled?(plugin) + !explicit_plugin_loading_order? || explicitly_registered?(plugin) + end + + def explicitly_registered?(plugin) + explicit_plugin_loading_order? && registered_plugin_names.include?(plugin.name) + end + + def registered_plugins_names_plugin?(plugin) + registered_plugin_names.include?(plugin.name) || registered_plugin_names.include?('all') + end + + # The plugins that have been explicitly listed with config.plugins. If this list is nil + # then it means the client does not care which plugins or in what order they are loaded, + # so we load all in alphabetical order. If it is an empty array, we load no plugins, if it is + # non empty, we load the named plugins in the order specified. + def registered_plugin_names + configuration.plugins ? configuration.plugins.map(&:to_s) : nil + end + + def loaded?(plugin_name) + initializer.loaded_plugins.detect { |plugin| plugin.name == plugin_name.to_s } + end + + def ensure_all_registered_plugins_are_loaded! + if explicit_plugin_loading_order? + if configuration.plugins.detect {|plugin| plugin != :all && !loaded?(plugin) } + missing_plugins = configuration.plugins - (plugins.map{|p| p.name.to_sym} + [:all]) + raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence(:locale => :en)}" + end + end + end + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin/locator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin/locator.rb new file mode 100644 index 00000000000..a6fc388a8e7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/plugin/locator.rb @@ -0,0 +1,100 @@ +module Rails + class Plugin + + # The Plugin::Locator class should be subclasses to provide custom plugin-finding + # abilities to Rails (i.e. loading plugins from Gems, etc). Each subclass should implement + # the located_plugins method, which return an array of Plugin objects that have been found. + class Locator + include Enumerable + + attr_reader :initializer + + def initialize(initializer) + @initializer = initializer + end + + # This method should return all the plugins which this Plugin::Locator can find + # These will then be used by the current Plugin::Loader, which is responsible for actually + # loading the plugins themselves + def plugins + raise "The `plugins' method must be defined by concrete subclasses of #{self.class}" + end + + def each(&block) + plugins.each(&block) + end + + def plugin_names + plugins.map(&:name) + end + end + + # The Rails::Plugin::FileSystemLocator will try to locate plugins by examining the directories + # in the paths given in configuration.plugin_paths. Any plugins that can be found are returned + # in a list. + # + # The criteria for a valid plugin in this case is found in Rails::Plugin#valid?, although + # other subclasses of Rails::Plugin::Locator can of course use different conditions. + class FileSystemLocator < Locator + + # Returns all the plugins which can be loaded in the filesystem, under the paths given + # by configuration.plugin_paths. + def plugins + initializer.configuration.plugin_paths.flatten.inject([]) do |plugins, path| + plugins.concat locate_plugins_under(path) + plugins + end.flatten + end + + private + + # Attempts to create a plugin from the given path. If the created plugin is valid? + # (see Rails::Plugin#valid?) then the plugin instance is returned; otherwise nil. + def create_plugin(path) + plugin = Rails::Plugin.new(path) + plugin.valid? ? plugin : nil + end + + # This starts at the base path looking for valid plugins (see Rails::Plugin#valid?). + # Since plugins can be nested arbitrarily deep within an unspecified number of intermediary + # directories, this method runs recursively until it finds a plugin directory, e.g. + # + # locate_plugins_under('vendor/plugins/acts/acts_as_chunky_bacon') + # => + # + def locate_plugins_under(base_path) + Dir.glob(File.join(base_path, '*')).sort.inject([]) do |plugins, path| + if plugin = create_plugin(path) + plugins << plugin + elsif File.directory?(path) + plugins.concat locate_plugins_under(path) + end + plugins + end + end + end + + # The GemLocator scans all the loaded RubyGems, looking for gems with + # a rails/init.rb file. + class GemLocator < Locator + def plugins + gem_index = initializer.configuration.gems.inject({}) { |memo, gem| memo.update gem.specification => gem } + specs = gem_index.keys + specs += Gem.loaded_specs.values.select do |spec| + spec.loaded_from && # prune stubs + File.exist?(File.join(spec.full_gem_path, "rails", "init.rb")) + end + specs.compact! + + require "rubygems/dependency_list" + + deps = Gem::DependencyList.new + deps.add(*specs) unless specs.empty? + + deps.dependency_order.collect do |spec| + Rails::GemPlugin.new(spec, gem_index[spec]) + end + end + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack.rb new file mode 100644 index 00000000000..9705f65e52b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack.rb @@ -0,0 +1,8 @@ +module Rails + module Rack + autoload :Debugger, "rails/rack/debugger" + autoload :LogTailer, "rails/rack/log_tailer" + autoload :Metal, "rails/rack/metal" + autoload :Static, "rails/rack/static" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/debugger.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/debugger.rb new file mode 100644 index 00000000000..0a7b4055534 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/debugger.rb @@ -0,0 +1,23 @@ +module Rails + module Rack + class Debugger + def initialize(app) + @app = app + + ARGV.clear # clear ARGV so that script/server options aren't passed to IRB + + require_library_or_gem 'ruby-debug' + ::Debugger.start + ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings) + puts "=> Debugger enabled" + rescue Exception + puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'" + exit + end + + def call(env) + @app.call(env) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/log_tailer.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/log_tailer.rb new file mode 100644 index 00000000000..a237cee6bc6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/log_tailer.rb @@ -0,0 +1,35 @@ +module Rails + module Rack + class LogTailer + EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log" + + def initialize(app, log = nil) + @app = app + + path = Pathname.new(log || EnvironmentLog).cleanpath + @cursor = ::File.size(path) + @last_checked = Time.now.to_f + + @file = ::File.open(path, 'r') + end + + def call(env) + response = @app.call(env) + tail_log + response + end + + def tail_log + @file.seek @cursor + + mod = @file.mtime.to_f + if mod > @last_checked + contents = @file.read + @last_checked = mod + @cursor += contents.size + $stdout.print contents + end + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/metal.rb new file mode 100644 index 00000000000..8719a5c5812 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/metal.rb @@ -0,0 +1,51 @@ +require 'active_support/ordered_hash' + +module Rails + module Rack + class Metal + NotFoundResponse = [404, {}, []].freeze + NotFound = lambda { NotFoundResponse } + + cattr_accessor :metal_paths + self.metal_paths = ["#{Rails.root}/app/metal"] + cattr_accessor :requested_metals + + def self.metals + matcher = /#{Regexp.escape('/app/metal/')}(.*)\.rb\Z/ + metal_glob = metal_paths.map{ |base| "#{base}/**/*.rb" } + all_metals = {} + + metal_glob.each do |glob| + Dir[glob].sort.map do |file| + file = file.match(matcher)[1] + all_metals[file.camelize] = file + end + end + + load_list = requested_metals || all_metals.keys + + load_list.map do |requested_metal| + if metal = all_metals[requested_metal] + require_dependency metal + requested_metal.constantize + end + end.compact + end + + def initialize(app) + @app = app + @metals = ActiveSupport::OrderedHash.new + self.class.metals.each { |app| @metals[app] = true } + freeze + end + + def call(env) + @metals.keys.each do |app| + result = app.call(env) + return result unless result[0].to_i == 404 + end + @app.call(env) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/static.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/static.rb new file mode 100644 index 00000000000..f07c6beb5e4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/rack/static.rb @@ -0,0 +1,46 @@ +require 'rack/utils' + +module Rails + module Rack + class Static + FILE_METHODS = %w(GET HEAD).freeze + + def initialize(app) + @app = app + @file_server = ::Rack::File.new(File.join(RAILS_ROOT, "public")) + end + + def call(env) + path = env['PATH_INFO'].chomp('/') + method = env['REQUEST_METHOD'] + + if FILE_METHODS.include?(method) + if file_exist?(path) + return @file_server.call(env) + else + cached_path = directory_exist?(path) ? "#{path}/index" : path + cached_path += ::ActionController::Base.page_cache_extension + + if file_exist?(cached_path) + env['PATH_INFO'] = cached_path + return @file_server.call(env) + end + end + end + + @app.call(env) + end + + private + def file_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.file?(full_path) && File.readable?(full_path) + end + + def directory_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.directory?(full_path) && File.readable?(full_path) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/vendor_gem_source_index.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/vendor_gem_source_index.rb new file mode 100644 index 00000000000..5b7721f303f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/vendor_gem_source_index.rb @@ -0,0 +1,140 @@ +require 'rubygems' +require 'yaml' + +module Rails + + class VendorGemSourceIndex + # VendorGemSourceIndex acts as a proxy for the Gem source index, allowing + # gems to be loaded from vendor/gems. Rather than the standard gem repository format, + # vendor/gems contains unpacked gems, with YAML specifications in .specification in + # each gem directory. + include Enumerable + + attr_reader :installed_source_index + attr_reader :vendor_source_index + + @@silence_spec_warnings = false + + def self.silence_spec_warnings + @@silence_spec_warnings + end + + def self.silence_spec_warnings=(v) + @@silence_spec_warnings = v + end + + def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path) + @installed_source_index = installed_index + @vendor_dir = vendor_dir + refresh! + end + + def refresh! + # reload the installed gems + @installed_source_index.refresh! + vendor_gems = {} + + # handle vendor Rails gems - they are identified by having loaded_from set to "" + # we add them manually to the list, so that other gems can find them via dependencies + Gem.loaded_specs.each do |n, s| + next unless s.loaded_from.empty? + vendor_gems[s.full_name] = s + end + + # load specifications from vendor/gems + Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d| + dir_name = File.basename(d) + dir_version = version_for_dir(dir_name) + spec = load_specification(d) + if spec + if spec.full_name != dir_name + # mismatched directory name and gem spec - produced by 2.1.0-era unpack code + if dir_version + # fix the spec version - this is not optimal (spec.files may be wrong) + # but it's better than breaking apps. Complain to remind users to get correct specs. + # use ActiveSupport::Deprecation.warn, as the logger is not set yet + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has a mismatched specification file."+ + " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings + spec.version = dir_version + else + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems is not in a versioned directory"+ + "(should be #{spec.full_name}).") unless @@silence_spec_warnings + # continue, assume everything is OK + end + end + else + # no spec - produced by early-2008 unpack code + # emulate old behavior, and complain. + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has no specification file."+ + " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings + if dir_version + spec = Gem::Specification.new + spec.version = dir_version + spec.require_paths = ['lib'] + ext_path = File.join(d, 'ext') + spec.require_paths << 'ext' if File.exist?(ext_path) + spec.name = /^(.*)-[^-]+$/.match(dir_name)[1] + files = ['lib'] + # set files to everything in lib/ + files += Dir[File.join(d, 'lib', '*')].map { |v| v.gsub(/^#{d}\//, '') } + files += Dir[File.join(d, 'ext', '*')].map { |v| v.gsub(/^#{d}\//, '') } if ext_path + spec.files = files + else + $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+ + " Giving up.") unless @@silence_spec_warnings + next + end + end + spec.loaded_from = File.join(d, '.specification') + # finally, swap out full_gem_path + # it would be better to use a Gem::Specification subclass, but the YAML loads an explicit class + class << spec + def full_gem_path + path = File.join installation_path, full_name + return path if File.directory? path + File.join installation_path, original_name + end + end + vendor_gems[File.basename(d)] = spec + end + @vendor_source_index = Gem::SourceIndex.new(vendor_gems) + end + + def version_for_dir(d) + matches = /-([^-]+)$/.match(d) + Gem::Version.new(matches[1]) if matches + end + + def load_specification(gem_dir) + spec_file = File.join(gem_dir, '.specification') + YAML.load_file(spec_file) if File.exist?(spec_file) + end + + def find_name(*args) + @installed_source_index.find_name(*args) + @vendor_source_index.find_name(*args) + end + + def search(*args) + # look for vendor gems, and then installed gems - later elements take priority + @installed_source_index.search(*args) + @vendor_source_index.search(*args) + end + + def each(&block) + @vendor_source_index.each(&block) + @installed_source_index.each(&block) + end + + def add_spec(spec) + @vendor_source_index.add_spec spec + end + + def remove_spec(spec) + @vendor_source_index.remove_spec spec + end + + def size + @vendor_source_index.size + @installed_source_index.size + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/version.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/version.rb new file mode 100644 index 00000000000..98e4e03a830 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails/version.rb @@ -0,0 +1,9 @@ +module Rails + module VERSION #:nodoc: + MAJOR = 2 + MINOR = 3 + TINY = 5 + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator.rb new file mode 100644 index 00000000000..9f0ffc1562c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator.rb @@ -0,0 +1,43 @@ +#-- +# Copyright (c) 2004 Jeremy Kemper +# +# 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. +#++ + +$:.unshift(File.dirname(__FILE__)) +$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib") + +begin + require 'active_support' +rescue LoadError + require 'rubygems' + gem 'activesupport' +end + +require 'rails_generator/base' +require 'rails_generator/lookup' +require 'rails_generator/commands' + +Rails::Generator::Base.send(:include, Rails::Generator::Lookup) +Rails::Generator::Base.send(:include, Rails::Generator::Commands) + +# Set up a default logger for convenience. +require 'rails_generator/simple_logger' +Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(STDOUT) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/base.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/base.rb new file mode 100644 index 00000000000..dd75f05c7bd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/base.rb @@ -0,0 +1,266 @@ +require File.dirname(__FILE__) + '/options' +require File.dirname(__FILE__) + '/manifest' +require File.dirname(__FILE__) + '/spec' +require File.dirname(__FILE__) + '/generated_attribute' + +module Rails + # Rails::Generator is a code generation platform tailored for the Rails + # web application framework. Generators are easily invoked within Rails + # applications to add and remove components such as models and controllers. + # New generators are easy to create and may be distributed as RubyGems, + # tarballs, or Rails plugins for inclusion system-wide, per-user, + # or per-application. + # + # For actual examples see the rails_generator/generators directory in the + # Rails source (or the +railties+ directory if you have frozen the Rails + # source in your application). + # + # Generators may subclass other generators to provide variations that + # require little or no new logic but replace the template files. + # + # For a RubyGem, put your generator class and templates in the +lib+ + # directory. For a Rails plugin, make a +generators+ directory at the + # root of your plugin. + # + # The layout of generator files can be seen in the built-in + # +controller+ generator: + # + # generators/ + # components/ + # controller/ + # controller_generator.rb + # templates/ + # controller.rb + # functional_test.rb + # helper.rb + # view.html.erb + # + # The directory name (+controller+) matches the name of the generator file + # (controller_generator.rb) and class (ControllerGenerator). The files + # that will be copied or used as templates are stored in the +templates+ + # directory. + # + # The filenames of the templates don't matter, but choose something that + # will be self-explanatory since you will be referencing these in the + # +manifest+ method inside your generator subclass. + # + # + module Generator + class GeneratorError < StandardError; end + class UsageError < GeneratorError; end + + + # The base code generator is bare-bones. It sets up the source and + # destination paths and tells the logger whether to keep its trap shut. + # + # It's useful for copying files such as stylesheets, images, or + # javascripts. + # + # For more comprehensive template-based passive code generation with + # arguments, you'll want Rails::Generator::NamedBase. + # + # Generators create a manifest of the actions they perform then hand + # the manifest to a command which replays the actions to do the heavy + # lifting (such as checking for existing files or creating directories + # if needed). Create, destroy, and list commands are included. Since a + # single manifest may be used by any command, creating new generators is + # as simple as writing some code templates and declaring what you'd like + # to do with them. + # + # The manifest method must be implemented by subclasses, returning a + # Rails::Generator::Manifest. The +record+ method is provided as a + # convenience for manifest creation. Example: + # + # class StylesheetGenerator < Rails::Generator::Base + # def manifest + # record do |m| + # m.directory('public/stylesheets') + # m.file('application.css', 'public/stylesheets/application.css') + # end + # end + # end + # + # See Rails::Generator::Commands::Create for a list of methods available + # to the manifest. + class Base + include Options + + # Declare default options for the generator. These options + # are inherited to subclasses. + default_options :collision => :ask, :quiet => false + + # A logger instance available everywhere in the generator. + cattr_accessor :logger + + # Every generator that is dynamically looked up is tagged with a + # Spec describing where it was found. + class_inheritable_accessor :spec + + attr_reader :source_root, :destination_root, :args + + def initialize(runtime_args, runtime_options = {}) + @args = runtime_args + parse!(@args, runtime_options) + + # Derive source and destination paths. + @source_root = options[:source] || File.join(spec.path, 'templates') + if options[:destination] + @destination_root = options[:destination] + elsif defined? ::RAILS_ROOT + @destination_root = ::RAILS_ROOT + end + + # Silence the logger if requested. + logger.quiet = options[:quiet] + + # Raise usage error if help is requested. + usage if options[:help] + end + + # Generators must provide a manifest. Use the +record+ method to create + # a new manifest and record your generator's actions. + def manifest + raise NotImplementedError, "No manifest for '#{spec.name}' generator." + end + + # Return the full path from the source root for the given path. + # Example for source_root = '/source': + # source_path('some/path.rb') == '/source/some/path.rb' + # + # The given path may include a colon ':' character to indicate that + # the file belongs to another generator. This notation allows any + # generator to borrow files from another. Example: + # source_path('model:fixture.yml') = '/model/source/path/fixture.yml' + def source_path(relative_source) + # Check whether we're referring to another generator's file. + name, path = relative_source.split(':', 2) + + # If not, return the full path to our source file. + if path.nil? + File.join(source_root, name) + + # Otherwise, ask our referral for the file. + else + # FIXME: this is broken, though almost always true. Others' + # source_root are not necessarily the templates dir. + File.join(self.class.lookup(name).path, 'templates', path) + end + end + + # Return the full path from the destination root for the given path. + # Example for destination_root = '/dest': + # destination_path('some/path.rb') == '/dest/some/path.rb' + def destination_path(relative_destination) + File.join(destination_root, relative_destination) + end + + def after_generate + end + + protected + # Convenience method for generator subclasses to record a manifest. + def record + Rails::Generator::Manifest.new(self) { |m| yield m } + end + + # Override with your own usage banner. + def banner + "Usage: #{$0} #{spec.name} [options]" + end + + # Read USAGE from file in generator base path. + def usage_message + File.read(File.join(spec.path, 'USAGE')) rescue '' + end + end + + + # The base generator for named components: models, controllers, mailers, + # etc. The target name is taken as the first argument and inflected to + # singular, plural, class, file, and table forms for your convenience. + # The remaining arguments are aliased to +actions+ as an array for + # controller and mailer convenience. + # + # Several useful local variables and methods are populated in the + # +initialize+ method. See below for a list of Attributes and + # External Aliases available to both the manifest and to all templates. + # + # If no name is provided, the generator raises a usage error with content + # optionally read from the USAGE file in the generator's base path. + # + # For example, the +controller+ generator takes the first argument as + # the name of the class and subsequent arguments as the names of + # actions to be generated: + # + # ./script/generate controller Article index new create + # + # See Rails::Generator::Base for a discussion of manifests, + # Rails::Generator::Commands::Create for methods available to the manifest, + # and Rails::Generator for a general discussion of generators. + class NamedBase < Base + attr_reader :name, :class_name, :singular_name, :plural_name, :table_name + attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth + alias_method :file_name, :singular_name + alias_method :actions, :args + + def initialize(runtime_args, runtime_options = {}) + super + + # Name argument is required. + usage if runtime_args.empty? + + @args = runtime_args.dup + base_name = @args.shift + assign_names!(base_name) + end + + protected + # Override with your own usage banner. + def banner + "Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]" + end + + def attributes + @attributes ||= @args.collect do |attribute| + Rails::Generator::GeneratedAttribute.new(*attribute.split(":")) + end + end + + + private + def assign_names!(name) + @name = name + base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name) + @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name) + @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name + if @class_nesting.empty? + @class_name = @class_name_without_nesting + else + @table_name = @class_nesting.underscore << "_" << @table_name + @class_name = "#{@class_nesting}::#{@class_name_without_nesting}" + end + @table_name.gsub! '/', '_' + end + + # Extract modules from filesystem-style or ruby-style path: + # good/fun/stuff + # Good::Fun::Stuff + # produce the same results. + def extract_modules(name) + modules = name.include?('/') ? name.split('/') : name.split('::') + name = modules.pop + path = modules.map { |m| m.underscore } + file_path = (path + [name.underscore]).join('/') + nesting = modules.map { |m| m.camelize }.join('::') + [name, path, file_path, nesting, modules.size] + end + + def inflect_names(name) + camel = name.camelize + under = camel.underscore + plural = under.pluralize + [camel, under, plural] + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/commands.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/commands.rb new file mode 100644 index 00000000000..60169705c5a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/commands.rb @@ -0,0 +1,632 @@ +require 'delegate' +require 'optparse' +require 'fileutils' +require 'tempfile' +require 'erb' + +module Rails + module Generator + module Commands + # Here's a convenient way to get a handle on generator commands. + # Command.instance('destroy', my_generator) instantiates a Destroy + # delegate of my_generator ready to do your dirty work. + def self.instance(command, generator) + const_get(command.to_s.camelize).new(generator) + end + + # Even more convenient access to commands. Include Commands in + # the generator Base class to get a nice #command instance method + # which returns a delegate for the requested command. + def self.included(base) + base.send(:define_method, :command) do |command| + Commands.instance(command, self) + end + end + + + # Generator commands delegate Rails::Generator::Base and implement + # a standard set of actions. Their behavior is defined by the way + # they respond to these actions: Create brings life; Destroy brings + # death; List passively observes. + # + # Commands are invoked by replaying (or rewinding) the generator's + # manifest of actions. See Rails::Generator::Manifest and + # Rails::Generator::Base#manifest method that generator subclasses + # are required to override. + # + # Commands allows generators to "plug in" invocation behavior, which + # corresponds to the GoF Strategy pattern. + class Base < DelegateClass(Rails::Generator::Base) + # Replay action manifest. RewindBase subclass rewinds manifest. + def invoke! + manifest.replay(self) + after_generate + end + + def dependency(generator_name, args, runtime_options = {}) + logger.dependency(generator_name) do + self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke! + end + end + + # Does nothing for all commands except Create. + def class_collisions(*class_names) + end + + # Does nothing for all commands except Create. + def readme(*args) + end + + protected + def current_migration_number + Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path| + n = File.basename(file_path).split('_', 2).first.to_i + if n > max then n else max end + end + end + + def next_migration_number + current_migration_number + 1 + end + + def migration_directory(relative_path) + directory(@migration_directory = relative_path) + end + + def existing_migrations(file_name) + Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/) + end + + def migration_exists?(file_name) + not existing_migrations(file_name).empty? + end + + def next_migration_string(padding = 3) + if ActiveRecord::Base.timestamped_migrations + Time.now.utc.strftime("%Y%m%d%H%M%S") + else + "%.#{padding}d" % next_migration_number + end + end + + def gsub_file(relative_destination, regexp, *args, &block) + path = destination_path(relative_destination) + content = File.read(path).gsub(regexp, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + + private + # Ask the user interactively whether to force collision. + def force_file_collision?(destination, src, dst, file_options = {}, &block) + $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] " + case $stdin.gets.chomp + when /\Ad\z/i + Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp| + temp.write render_file(src, file_options, &block) + temp.rewind + $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"` + end + puts "retrying" + raise 'retry diff' + when /\Aa\z/i + $stdout.puts "forcing #{spec.name}" + options[:collision] = :force + when /\Aq\z/i + $stdout.puts "aborting #{spec.name}" + raise SystemExit + when /\An\z/i then :skip + when /\Ay\z/i then :force + else + $stdout.puts <<-HELP +Y - yes, overwrite +n - no, do not overwrite +a - all, overwrite this and all others +q - quit, abort +d - diff, show the differences between the old and the new +h - help, show this help +HELP + raise 'retry' + end + rescue + retry + end + + def diff_cmd + ENV['RAILS_DIFF'] || 'diff -u' + end + + def render_template_part(template_options) + # Getting Sandbox to evaluate part template in it + part_binding = template_options[:sandbox].call.sandbox_binding + part_rel_path = template_options[:insert] + part_path = source_path(part_rel_path) + + # Render inner template within Sandbox binding + rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding) + begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id]) + end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id]) + begin_mark + rendered_part + end_mark + end + + def template_part_mark(name, id) + "\n" + end + end + + # Base class for commands which handle generator actions in reverse, such as Destroy. + class RewindBase < Base + # Rewind action manifest. + def invoke! + manifest.rewind(self) + end + end + + + # Create is the premier generator command. It copies files, creates + # directories, renders templates, and more. + class Create < Base + + # Check whether the given class names are already taken by + # Ruby or Rails. In the future, expand to check other namespaces + # such as the rest of the user's app. + def class_collisions(*class_names) + path = class_names.shift + class_names.flatten.each do |class_name| + # Convert to string to allow symbol arguments. + class_name = class_name.to_s + + # Skip empty strings. + next if class_name.strip.empty? + + # Split the class from its module nesting. + nesting = class_name.split('::') + name = nesting.pop + + # Hack to limit const_defined? to non-inherited on 1.9. + extra = [] + extra << false unless Object.method(:const_defined?).arity == 1 + + # Extract the last Module in the nesting. + last = nesting.inject(Object) { |last, nest| + break unless last.const_defined?(nest, *extra) + last.const_get(nest) + } + + # If the last Module exists, check whether the given + # class exists and raise a collision if so. + if last and last.const_defined?(name.camelize, *extra) + raise_class_collision(class_name) + end + end + end + + # Copy a file from source to destination with collision checking. + # + # The file_options hash accepts :chmod and :shebang and :collision options. + # :chmod sets the permissions of the destination file: + # file 'config/empty.log', 'log/test.log', :chmod => 0664 + # :shebang sets the #!/usr/bin/ruby line for scripts + # file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby' + # :collision sets the collision option only for the destination file: + # file 'settings/server.yml', 'config/server.yml', :collision => :skip + # + # Collisions are handled by checking whether the destination file + # exists and either skipping the file, forcing overwrite, or asking + # the user what to do. + def file(relative_source, relative_destination, file_options = {}, &block) + # Determine full paths for source and destination files. + source = source_path(relative_source) + destination = destination_path(relative_destination) + destination_exists = File.exist?(destination) + + # If source and destination are identical then we're done. + if destination_exists and identical?(source, destination, &block) + return logger.identical(relative_destination) + end + + # Check for and resolve file collisions. + if destination_exists + + # Make a choice whether to overwrite the file. :force and + # :skip already have their mind made up, but give :ask a shot. + choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask + when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block) + when :force then :force + when :skip then :skip + else raise "Invalid collision option: #{options[:collision].inspect}" + end + + # Take action based on our choice. Bail out if we chose to + # skip the file; otherwise, log our transgression and continue. + case choice + when :force then logger.force(relative_destination) + when :skip then return(logger.skip(relative_destination)) + else raise "Invalid collision choice: #{choice}.inspect" + end + + # File doesn't exist so log its unbesmirched creation. + else + logger.create relative_destination + end + + # If we're pretending, back off now. + return if options[:pretend] + + # Write destination file with optional shebang. Yield for content + # if block given so templaters may render the source file. If a + # shebang is requested, replace the existing shebang or insert a + # new one. + File.open(destination, 'wb') do |dest| + dest.write render_file(source, file_options, &block) + end + + # Optionally change permissions. + if file_options[:chmod] + FileUtils.chmod(file_options[:chmod], destination) + end + + # Optionally add file to subversion or git + system("svn add #{destination}") if options[:svn] + system("git add -v #{relative_destination}") if options[:git] + end + + # Checks if the source and the destination file are identical. If + # passed a block then the source file is a template that needs to first + # be evaluated before being compared to the destination. + def identical?(source, destination, &block) + return false if File.directory? destination + source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source) + destination = IO.read(destination) + source == destination + end + + # Generate a file for a Rails application using an ERuby template. + # Looks up and evaluates a template by name and writes the result. + # + # The ERB template uses explicit trim mode to best control the + # proliferation of whitespace in generated code. <%- trims leading + # whitespace; -%> trims trailing whitespace including one newline. + # + # A hash of template options may be passed as the last argument. + # The options accepted by the file are accepted as well as :assigns, + # a hash of variable bindings. Example: + # template 'foo', 'bar', :assigns => { :action => 'view' } + # + # Template is implemented in terms of file. It calls file with a + # block which takes a file handle and returns its rendered contents. + def template(relative_source, relative_destination, template_options = {}) + file(relative_source, relative_destination, template_options) do |file| + # Evaluate any assignments in a temporary, throwaway binding. + vars = template_options[:assigns] || {} + b = template_options[:binding] || binding + vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b } + + # Render the source file with the temporary binding. + ERB.new(file.read, nil, '-').result(b) + end + end + + def complex_template(relative_source, relative_destination, template_options = {}) + options = template_options.dup + options[:assigns] ||= {} + options[:assigns]['template_for_inclusion'] = render_template_part(template_options) + template(relative_source, relative_destination, options) + end + + # Create a directory including any missing parent directories. + # Always skips directories which exist. + def directory(relative_path) + path = destination_path(relative_path) + if File.exist?(path) + logger.exists relative_path + else + logger.create relative_path + unless options[:pretend] + FileUtils.mkdir_p(path) + # git doesn't require adding the paths, adding the files later will + # automatically do a path add. + + # Subversion doesn't do path adds, so we need to add + # each directory individually. + # So stack up the directory tree and add the paths to + # subversion in order without recursion. + if options[:svn] + stack = [relative_path] + until File.dirname(stack.last) == stack.last # dirname('.') == '.' + stack.push File.dirname(stack.last) + end + stack.reverse_each do |rel_path| + svn_path = destination_path(rel_path) + system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn')) + end + end + end + end + end + + # Creates symlink + def symlink(relative_source, relative_destination) + path = destination_path(relative_destination) + if File.exist?(path) + logger.exists relative_destination + else + logger.create relative_destination + FileUtils.ln_s(relative_source, path) + end + end + + # Display a README. + def readme(*relative_sources) + relative_sources.flatten.each do |relative_source| + logger.readme relative_source + puts File.read(source_path(relative_source)) unless options[:pretend] + end + end + + # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template. + def migration_template(relative_source, relative_destination, template_options = {}) + migration_directory relative_destination + migration_file_name = template_options[:migration_file_name] || file_name + raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name) + template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options) + end + + def route_resources(*resources) + resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + logger.route "map.resources #{resource_list}" + unless options[:pretend] + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n map.resources #{resource_list}\n" + end + end + end + + private + def render_file(path, options = {}) + File.open(path, 'rb') do |file| + if block_given? + yield file + else + content = '' + if shebang = options[:shebang] + content << "#!#{shebang}\n" + if line = file.gets + content << "line\n" if line !~ /^#!/ + end + end + content << file.read + end + end + end + + # Raise a usage error with an informative WordNet suggestion. + # Thanks to Florian Gross (flgr). + def raise_class_collision(class_name) + message = <([\w ]*?)<\/a>/s).uniq + end + end + rescue Exception + return nil + end + end + + + # Undo the actions performed by a generator. Rewind the action + # manifest and attempt to completely erase the results of each action. + class Destroy < RewindBase + # Remove a file if it exists and is a file. + def file(relative_source, relative_destination, file_options = {}) + destination = destination_path(relative_destination) + if File.exist?(destination) + logger.rm relative_destination + unless options[:pretend] + if options[:svn] + # If the file has been marked to be added + # but has not yet been checked in, revert and delete + if options[:svn][relative_destination] + system("svn revert #{destination}") + FileUtils.rm(destination) + else + # If the directory is not in the status list, it + # has no modifications so we can simply remove it + system("svn rm #{destination}") + end + elsif options[:git] + if options[:git][:new][relative_destination] + # file has been added, but not committed + system("git reset HEAD #{relative_destination}") + FileUtils.rm(destination) + elsif options[:git][:modified][relative_destination] + # file is committed and modified + system("git rm -f #{relative_destination}") + else + # If the directory is not in the status list, it + # has no modifications so we can simply remove it + system("git rm #{relative_destination}") + end + else + FileUtils.rm(destination) + end + end + else + logger.missing relative_destination + return + end + end + + # Templates are deleted just like files and the actions take the + # same parameters, so simply alias the file method. + alias_method :template, :file + + # Remove each directory in the given path from right to left. + # Remove each subdirectory if it exists and is a directory. + def directory(relative_path) + parts = relative_path.split('/') + until parts.empty? + partial = File.join(parts) + path = destination_path(partial) + if File.exist?(path) + if Dir[File.join(path, '*')].empty? + logger.rmdir partial + unless options[:pretend] + if options[:svn] + # If the directory has been marked to be added + # but has not yet been checked in, revert and delete + if options[:svn][relative_path] + system("svn revert #{path}") + FileUtils.rmdir(path) + else + # If the directory is not in the status list, it + # has no modifications so we can simply remove it + system("svn rm #{path}") + end + # I don't think git needs to remove directories?.. + # or maybe they have special consideration... + else + FileUtils.rmdir(path) + end + end + else + logger.notempty partial + end + else + logger.missing partial + end + parts.pop + end + end + + def complex_template(*args) + # nothing should be done here + end + + # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}". + def migration_template(relative_source, relative_destination, template_options = {}) + migration_directory relative_destination + + migration_file_name = template_options[:migration_file_name] || file_name + unless migration_exists?(migration_file_name) + puts "There is no migration named #{migration_file_name}" + return + end + + + existing_migrations(migration_file_name).each do |file_path| + file(relative_source, file_path, template_options) + end + end + + def route_resources(*resources) + resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') + look_for = "\n map.resources #{resource_list}\n" + logger.route "map.resources #{resource_list}" + gsub_file 'config/routes.rb', /(#{look_for})/mi, '' + end + end + + + # List a generator's action manifest. + class List < Base + def dependency(generator_name, args, options = {}) + logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})" + end + + def class_collisions(*class_names) + logger.class_collisions class_names.join(', ') + end + + def file(relative_source, relative_destination, options = {}) + logger.file relative_destination + end + + def template(relative_source, relative_destination, options = {}) + logger.template relative_destination + end + + def complex_template(relative_source, relative_destination, options = {}) + logger.template "#{options[:insert]} inside #{relative_destination}" + end + + def directory(relative_path) + logger.directory "#{destination_path(relative_path)}/" + end + + def readme(*args) + logger.readme args.join(', ') + end + + def migration_template(relative_source, relative_destination, options = {}) + migration_directory relative_destination + logger.migration_template file_name + end + + def route_resources(*resources) + resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') + logger.route "map.resources #{resource_list}" + end + end + + # Update generator's action manifest. + class Update < Create + def file(relative_source, relative_destination, options = {}) + # logger.file relative_destination + end + + def template(relative_source, relative_destination, options = {}) + # logger.template relative_destination + end + + def complex_template(relative_source, relative_destination, template_options = {}) + + begin + dest_file = destination_path(relative_destination) + source_to_update = File.readlines(dest_file).join + rescue Errno::ENOENT + logger.missing relative_destination + return + end + + logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}" + + begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id])) + end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id])) + + # Refreshing inner part of the template with freshly rendered part. + rendered_part = render_template_part(template_options) + source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part) + + File.open(dest_file, 'w') { |file| file.write(source_to_update) } + end + + def directory(relative_path) + # logger.directory "#{destination_path(relative_path)}/" + end + end + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generated_attribute.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generated_attribute.rb new file mode 100644 index 00000000000..a3d4a01142d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generated_attribute.rb @@ -0,0 +1,46 @@ +require 'optparse' + +module Rails + module Generator + class GeneratedAttribute + attr_accessor :name, :type, :column + + def initialize(name, type) + @name, @type = name, type.to_sym + @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type) + end + + def field_type + @field_type ||= case type + when :integer, :float, :decimal then :text_field + when :datetime, :timestamp, :time then :datetime_select + when :date then :date_select + when :string then :text_field + when :text then :text_area + when :boolean then :check_box + else + :text_field + end + end + + def default + @default ||= case type + when :integer then 1 + when :float then 1.5 + when :decimal then "9.99" + when :datetime, :timestamp, :time then Time.now.to_s(:db) + when :date then Date.today.to_s(:db) + when :string then "MyString" + when :text then "MyText" + when :boolean then false + else + "" + end + end + + def reference? + [ :references, :belongs_to ].include?(self.type) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/USAGE new file mode 100644 index 00000000000..36d6061a599 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/USAGE @@ -0,0 +1,9 @@ +Description: + The 'rails' command creates a new Rails application with a default + directory structure and configuration at the path you specify. + +Example: + rails ~/Code/Ruby/weblog + + This generates a skeletal Rails installation in ~/Code/Ruby/weblog. + See the README in the newly created application to get going. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/app_generator.rb new file mode 100644 index 00000000000..9bc461be69f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -0,0 +1,270 @@ +require 'rbconfig' +require File.dirname(__FILE__) + '/template_runner' +require 'digest/md5' +require 'active_support/secure_random' + +class AppGenerator < Rails::Generator::Base + DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + + DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) + DEFAULT_DATABASE = 'sqlite3' + + mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.." + default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE), + :shebang => DEFAULT_SHEBANG, :with_dispatchers => false, :freeze => false + + + def initialize(runtime_args, runtime_options = {}) + super + + usage if args.empty? + usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db])) + + @destination_root = args.shift + @app_name = File.basename(File.expand_path(@destination_root)) + end + + def manifest + record do |m| + create_directories(m) + create_root_files(m) + create_app_files(m) + create_config_files(m) + create_script_files(m) + create_test_files(m) + create_public_files(m) + create_documentation_file(m) + create_log_files(m) + + create_debian_railties_link(m) + end + end + + def after_generate + if options[:template] + Rails::TemplateRunner.new(options[:template], @destination_root) + end + end + + protected + def banner + "Usage: #{$0} /path/to/your/app [options]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("-r", "--ruby=path", String, + "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).", + "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v } + + opt.on("-d", "--database=name", String, + "Preconfigure for selected database (options: #{DATABASES.join('/')}).", + "Default: #{DEFAULT_DATABASE}") { |v| options[:db] = v } + + opt.on("-D", "--with-dispatchers", + "Add CGI/FastCGI/mod_ruby dispatches code to generated application skeleton", + "Default: false") { |v| options[:with_dispatchers] = v } + + opt.on("-f", "--freeze", + "Freeze Rails in vendor/rails from the gems generating the skeleton", + "Default: false") { |v| options[:freeze] = v } + + opt.on("-m", "--template=path", String, + "Use an application template that lives at path (can be a filesystem path or URL).", + "Default: (none)") { |v| options[:template] = v } + + end + + + private + def create_directories(m) + m.directory '' + + # Intermediate directories are automatically created so don't sweat their absence here. + %w( + app/controllers + app/helpers + app/models + app/views/layouts + config/environments + config/initializers + config/locales + db + doc + lib + lib/tasks + log + public/images + public/javascripts + public/stylesheets + script/performance + test/fixtures + test/functional + test/integration + test/performance + test/unit + vendor + vendor/plugins + tmp/sessions + tmp/sockets + tmp/cache + tmp/pids + ).each { |path| m.directory(path) } + end + + def create_root_files(m) + m.file "fresh_rakefile", "Rakefile" + m.file "README", "README" + end + + def create_app_files(m) + m.file "helpers/application_controller.rb", "app/controllers/application_controller.rb" + m.file "helpers/application_helper.rb", "app/helpers/application_helper.rb" + end + + def create_config_files(m) + create_database_configuration_file(m) + create_routes_file(m) + create_locale_file(m) + create_seeds_file(m) + create_initializer_files(m) + create_environment_files(m) + end + + def create_documentation_file(m) + m.file "doc/README_FOR_APP", "doc/README_FOR_APP" + end + + def create_log_files(m) + %w( server production development test ).each do |file| + m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 + end + end + + def create_public_files(m) + create_dispatch_files(m) + create_error_files(m) + create_welcome_file(m) + create_browser_convention_files(m) + create_rails_image(m) + create_javascript_files(m) + end + + def create_script_files(m) + %w( + about console dbconsole destroy generate runner server plugin + performance/benchmarker performance/profiler + ).each do |file| + m.file "bin/#{file}", "script/#{file}", { + :chmod => 0755, + :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] + } + end + end + + def create_test_files(m) + m.file "helpers/test_helper.rb", "test/test_helper.rb" + m.file "helpers/performance_test.rb", "test/performance/browsing_test.rb" + end + + + def create_database_configuration_file(m) + m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { + :app_name => @app_name, + :socket => options[:db] == "mysql" ? mysql_socket_location : nil } + end + + def create_routes_file(m) + m.file "configs/routes.rb", "config/routes.rb" + end + + def create_seeds_file(m) + m.file "configs/seeds.rb", "db/seeds.rb" + end + + def create_initializer_files(m) + %w( + backtrace_silencers + inflections + mime_types + new_rails_defaults + ).each do |initializer| + m.file "configs/initializers/#{initializer}.rb", "config/initializers/#{initializer}.rb" + end + + m.template "configs/initializers/session_store.rb", "config/initializers/session_store.rb", + :assigns => { :app_name => @app_name, :app_secret => ActiveSupport::SecureRandom.hex(64) } + end + + def create_locale_file(m) + m.file "configs/locales/en.yml", "config/locales/en.yml" + end + + def create_environment_files(m) + m.template "environments/environment.rb", "config/environment.rb", + :assigns => { :freeze => options[:freeze] } + + m.file "environments/boot.rb", "config/boot.rb" + m.file "environments/production.rb", "config/environments/production.rb" + m.file "environments/development.rb", "config/environments/development.rb" + m.file "environments/test.rb", "config/environments/test.rb" + end + + + def create_dispatch_files(m) + if options[:with_dispatchers] + dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } + + m.file "dispatches/config.ru", "config.ru" + m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options + m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options + m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options + end + end + + def create_error_files(m) + %w( 404 422 500 ).each do |file| + m.file "html/#{file}.html", "public/#{file}.html" + end + end + + def create_welcome_file(m) + m.file 'html/index.html', 'public/index.html' + end + + def create_browser_convention_files(m) + m.file "html/favicon.ico", "public/favicon.ico" + m.file "html/robots.txt", "public/robots.txt" + end + + def create_rails_image(m) + m.file "html/images/rails.png", "public/images/rails.png" + end + + def create_javascript_files(m) + %w( prototype effects dragdrop controls application ).each do |javascript| + m.file "html/javascripts/#{javascript}.js", "public/javascripts/#{javascript}.js" + end + end + + def create_debian_railties_link(m) + m.symlink "/usr/share/rails-ruby1.8", "vendor/rails" + m.symlink "/usr/share/rails-ruby1.8/railties", "vendor/railties" + end + + + def mysql_socket_location + [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/scm/git.rb new file mode 100644 index 00000000000..a53494de507 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/scm/git.rb @@ -0,0 +1,18 @@ +STDOUT.sync = true + +module Rails + class Git < Scm + def self.clone(repos, branch=nil) + system "git clone #{repos}" + + if branch + system "cd #{repos.split('/').last}/" + system "git checkout #{branch}" + end + end + + def self.run(command) + system "git #{command}" + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/scm/scm.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/scm/scm.rb new file mode 100644 index 00000000000..f6c08cad39c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/scm/scm.rb @@ -0,0 +1,8 @@ +module Rails + class Scm + private + def self.hash_to_parameters(hash) + hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ") + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/template_runner.rb new file mode 100644 index 00000000000..40f21cc457c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/applications/app/template_runner.rb @@ -0,0 +1,401 @@ +require File.dirname(__FILE__) + '/scm/scm' +require File.dirname(__FILE__) + '/scm/git' +require File.dirname(__FILE__) + '/scm/svn' + +require 'open-uri' +require 'fileutils' + +module Rails + class TemplateRunner + attr_reader :root + attr_writer :logger + + def initialize(template, root = '') # :nodoc: + @root = File.expand_path(File.directory?(root) ? root : File.join(Dir.pwd, root)) + + log 'applying', "template: #{template}" + + load_template(template) + + log 'applied', "#{template}" + end + + def load_template(template) + begin + code = open(template).read + in_root { self.instance_eval(code) } + rescue LoadError, Errno::ENOENT => e + raise "The template [#{template}] could not be loaded. Error: #{e}" + end + end + + # Create a new file in the Rails project folder. Specify the + # relative path from RAILS_ROOT. Data is the return value of a block + # or a data string. + # + # ==== Examples + # + # file("lib/fun_party.rb") do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # file("config/apach.conf", "your apache config") + # + def file(filename, data = nil, log_action = true, &block) + log 'file', filename if log_action + dir, file = [File.dirname(filename), File.basename(filename)] + + inside(dir) do + File.open(file, "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Install a plugin. You must provide either a Subversion url or Git url. + # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned. + # + # ==== Examples + # + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true + # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + # + def plugin(name, options) + log 'plugin', name + + if options[:git] && options[:submodule] + in_root do + Git.run("submodule add #{options[:git]} vendor/plugins/#{name}") + end + elsif options[:git] || options[:svn] + in_root do + run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false) + end + else + log "! no git or svn provided for #{name}. skipping..." + end + end + + # Adds an entry into config/environment.rb for the supplied gem : + def gem(name, options = {}) + log 'gem', name + env = options.delete(:env) + + gems_code = "config.gem '#{name}'" + + if options.any? + opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ") + gems_code << ", #{opts}" + end + + environment gems_code, :env => env + end + + # Adds a line inside the Initializer block for config/environment.rb. Used by #gem + # If options :env is specified, the line is appended to the corresponding + # file in config/environments/#{env}.rb + def environment(data = nil, options = {}, &block) + sentinel = 'Rails::Initializer.run do |config|' + + data = block.call if !data && block_given? + + in_root do + if options[:env].nil? + gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n " << data + end + else + Array.wrap(options[:env]).each do|env| + append_file "config/environments/#{env}.rb", "\n#{data}" + end + end + end + end + + # Run a command in git. + # + # ==== Examples + # + # git :init + # git :add => "this.file that.rb" + # git :add => "onefile.rb", :rm => "badfile.cxx" + # + def git(command = {}) + in_root do + if command.is_a?(Symbol) + log 'running', "git #{command}" + Git.run(command.to_s) + else + command.each do |command, options| + log 'running', "git #{command} #{options}" + Git.run("#{command} #{options}") + end + end + end + end + + # Create a new file in the vendor/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # vendor("sekrit.rb") do + # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" + # "salt = '#{sekrit_salt}'" + # end + # + # vendor("foreign.rb", "# Foreign code is fun") + # + def vendor(filename, data = nil, &block) + log 'vendoring', filename + file("vendor/#{filename}", data, false, &block) + end + + # Create a new file in the lib/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # lib("crypto.rb") do + # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" + # end + # + # lib("foreign.rb", "# Foreign code is fun") + # + def lib(filename, data = nil, &block) + log 'lib', filename + file("lib/#{filename}", data, false, &block) + end + + # Create a new Rakefile with the provided code (either in a block or a string). + # + # ==== Examples + # + # rakefile("bootstrap.rake") do + # project = ask("What is the UNIX name of your project?") + # + # <<-TASK + # namespace :#{project} do + # task :bootstrap do + # puts "i like boots!" + # end + # end + # TASK + # end + # + # rakefile("seed.rake", "puts 'im plantin ur seedz'") + # + def rakefile(filename, data = nil, &block) + log 'rakefile', filename + file("lib/tasks/#{filename}", data, false, &block) + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # ==== Examples + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do + # data << "#{const} = :entp" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + # + def initializer(filename, data = nil, &block) + log 'initializer', filename + file("config/initializers/#{filename}", data, false, &block) + end + + # Generate something using a generator from Rails or a plugin. + # The second parameter is the argument string that is passed to + # the generator or an Array that is joined. + # + # ==== Example + # + # generate(:authenticated, "user session") + # + def generate(what, *args) + log 'generating', what + argument = args.map(&:to_s).flatten.join(" ") + + in_root { run_ruby_script("script/generate #{what} #{argument}", false) } + end + + # Executes a command + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + def run(command, log_action = true) + log 'executing', "#{command} from #{Dir.pwd}" if log_action + `#{command}` + end + + # Executes a ruby script (taking into account WIN32 platform quirks) + def run_ruby_script(command, log_action = true) + ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : '' + run("#{ruby_command}#{command}", log_action) + end + + # Runs the supplied rake task + # + # ==== Example + # + # rake("db:migrate") + # rake("db:migrate", :env => "production") + # rake("gems:install", :sudo => true) + # + def rake(command, options = {}) + log 'rake', command + env = options[:env] || 'development' + sudo = options[:sudo] ? 'sudo ' : '' + in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) } + end + + # Just run the capify command in root + # + # ==== Example + # + # capify! + # + def capify! + log 'capifying' + in_root { run('capify .', false) } + end + + # Add Rails to /vendor/rails + # + # ==== Example + # + # freeze! + # + def freeze!(args = {}) + log 'vendor', 'rails edge' + in_root { run('rake rails:freeze:edge', false) } + end + + # Make an entry in Rails routing file conifg/routes.rb + # + # === Example + # + # route "map.root :controller => :welcome" + # + def route(routing_code) + log 'route', routing_code + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + in_root do + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{routing_code}\n" + end + end + end + + protected + + # Get a user's input + # + # ==== Example + # + # answer = ask("Should I freeze the latest Rails?") + # freeze! if ask("Should I freeze the latest Rails?") == "yes" + # + def ask(string) + log '', string + STDIN.gets.strip + end + + # Do something in the root of the Rails application or + # a provided subfolder; the full path is yielded to the block you provide. + # The path is set back to the previous path when the method exits. + def inside(dir = '', &block) + folder = File.join(root, dir) + FileUtils.mkdir_p(folder) unless File.exist?(folder) + FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } + end + + def in_root + FileUtils.cd(root) { yield } + end + + # Helper to test if the user says yes(y)? + # + # ==== Example + # + # freeze! if yes?("Should I freeze the latest Rails?") + # + def yes?(question) + answer = ask(question).downcase + answer == "y" || answer == "yes" + end + + # Helper to test if the user does NOT say yes(y)? + # + # ==== Example + # + # capify! if no?("Will you be using vlad to deploy your application?") + # + def no?(question) + !yes?(question) + end + + # Run a regular expression replacement on a file + # + # ==== Example + # + # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + def gsub_file(relative_destination, regexp, *args, &block) + path = destination_path(relative_destination) + content = File.read(path).gsub(regexp, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + + # Append text to a file + # + # ==== Example + # + # append_file 'config/environments/test.rb', 'config.gem "rspec"' + # + def append_file(relative_destination, data) + path = destination_path(relative_destination) + File.open(path, 'ab') { |file| file.write(data) } + end + + def destination_path(relative_destination) + File.join(root, relative_destination) + end + + def log(action, message = '') + logger.log(action, message) + end + + def logger + @logger ||= Rails::Generator::Base.logger + end + + def logger + @logger ||= if defined?(Rails::Generator::Base) + Rails::Generator::Base.logger + else + require 'rails_generator/simple_logger' + Rails::Generator::SimpleLogger.new(STDOUT) + end + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/USAGE new file mode 100644 index 00000000000..362872e84ab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/USAGE @@ -0,0 +1,30 @@ +Description: + Stubs out a new controller and its views. Pass the controller name, either + CamelCased or under_scored, and a list of views as arguments. + + To create a controller within a module, specify the controller name as a + path like 'parent_module/controller_name'. + + This generates a controller class in app/controllers, view templates in + app/views/controller_name, a helper class in app/helpers, a functional + test suite in test/functional and a helper test suite in test/unit/helpers. + +Example: + `./script/generate controller CreditCard open debit credit close` + + Credit card controller with URLs like /credit_card/debit. + Controller: app/controllers/credit_card_controller.rb + Functional Test: test/functional/credit_card_controller_test.rb + Views: app/views/credit_card/debit.html.erb [...] + Helper: app/helpers/credit_card_helper.rb + Helper Test: test/unit/helpers/credit_card_helper_test.rb + +Modules Example: + `./script/generate controller 'admin/credit_card' suspend late_fee` + + Credit card admin controller with URLs /admin/credit_card/suspend. + Controller: app/controllers/admin/credit_card_controller.rb + Functional Test: test/functional/admin/credit_card_controller_test.rb + Views: app/views/admin/credit_card/debit.html.erb [...] + Helper: app/helpers/admin/credit_card_helper.rb + Helper Test: test/unit/helpers/admin/credit_card_helper_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/controller_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/controller_generator.rb new file mode 100644 index 00000000000..dc126e8a989 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/controller_generator.rb @@ -0,0 +1,43 @@ +class ControllerGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper", "#{class_name}HelperTest" + + # Controller, helper, views, and test directories. + m.directory File.join('app/controllers', class_path) + m.directory File.join('app/helpers', class_path) + m.directory File.join('app/views', class_path, file_name) + m.directory File.join('test/functional', class_path) + m.directory File.join('test/unit/helpers', class_path) + + # Controller class, functional test, and helper class. + m.template 'controller.rb', + File.join('app/controllers', + class_path, + "#{file_name}_controller.rb") + + m.template 'functional_test.rb', + File.join('test/functional', + class_path, + "#{file_name}_controller_test.rb") + + m.template 'helper.rb', + File.join('app/helpers', + class_path, + "#{file_name}_helper.rb") + + m.template 'helper_test.rb', + File.join('test/unit/helpers', + class_path, + "#{file_name}_helper_test.rb") + + # View template for each action. + actions.each do |action| + path = File.join('app/views', class_path, file_name, "#{action}.html.erb") + m.template 'view.html.erb', path, + :assigns => { :action => action, :path => path } + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/controller.rb new file mode 100644 index 00000000000..cda2659e693 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/controller.rb @@ -0,0 +1,7 @@ +class <%= class_name %>Controller < ApplicationController +<% for action in actions -%> + def <%= action %> + end + +<% end -%> +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb new file mode 100644 index 00000000000..62fa5d86fdd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>ControllerTest < ActionController::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/helper.rb new file mode 100644 index 00000000000..3fe2ecdc74c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb new file mode 100644 index 00000000000..591e40900e9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb new file mode 100644 index 00000000000..ad85431f986 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb @@ -0,0 +1,2 @@ +

        <%= class_name %>#<%= action %>

        +

        Find me in <%= path %>

        diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/USAGE new file mode 100644 index 00000000000..ef27ca617e5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/USAGE @@ -0,0 +1,24 @@ +Description: + Stubs out a new helper. Pass the helper name, either + CamelCased or under_scored. + + To create a helper within a module, specify the helper name as a + path like 'parent_module/helper_name'. + + This generates a helper class in app/helpers and a helper test + suite in test/unit/helpers. + +Example: + `./script/generate helper CreditCard` + + Credit card helper. + Helper: app/helpers/credit_card_helper.rb + Test: test/unit/helpers/credit_card_helper_test.rb + +Modules Example: + `./script/generate helper 'admin/credit_card'` + + Credit card admin helper. + Helper: app/helpers/admin/credit_card_helper.rb + Test: test/unit/helpers/admin/credit_card_helper_test.rb + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/helper_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/helper_generator.rb new file mode 100644 index 00000000000..f7831f7c7ab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/helper_generator.rb @@ -0,0 +1,25 @@ +class HelperGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_path, "#{class_name}Helper", "#{class_name}HelperTest" + + # Helper and helper test directories. + m.directory File.join('app/helpers', class_path) + m.directory File.join('test/unit/helpers', class_path) + + # Helper and helper test class. + + m.template 'helper.rb', + File.join('app/helpers', + class_path, + "#{file_name}_helper.rb") + + m.template 'helper_test.rb', + File.join('test/unit/helpers', + class_path, + "#{file_name}_helper_test.rb") + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/templates/helper.rb new file mode 100644 index 00000000000..3fe2ecdc74c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb new file mode 100644 index 00000000000..591e40900e9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/USAGE new file mode 100644 index 00000000000..09e2691f690 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/USAGE @@ -0,0 +1,8 @@ +Description: + Stubs out a new integration test. Pass the name of the test, either + CamelCased or under_scored, as an argument. The new test class is + generated in test/integration/testname_test.rb + +Example: + `./script/generate integration_test GeneralStories` creates a GeneralStories + integration test in test/integration/general_stories_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb new file mode 100644 index 00000000000..44323f28cac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb @@ -0,0 +1,16 @@ +class IntegrationTestGenerator < Rails::Generator::NamedBase + default_options :skip_migration => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # integration test directory + m.directory File.join('test/integration', class_path) + + # integration test stub + m.template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb new file mode 100644 index 00000000000..2c57158b1c4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb @@ -0,0 +1,10 @@ +require 'test_helper' + +class <%= class_name %>Test < ActionController::IntegrationTest + fixtures :all + + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/USAGE new file mode 100644 index 00000000000..61a649ed4d2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/USAGE @@ -0,0 +1,16 @@ +Description: + Stubs out a new mailer and its views. Pass the mailer name, either + CamelCased or under_scored, and an optional list of emails as arguments. + + This generates a mailer class in app/models, view templates in + app/views/mailer_name, a unit test in test/unit, and fixtures in + test/fixtures. + +Example: + `./script/generate mailer Notifications signup forgot_password invoice` + + creates a Notifications mailer class, views, test, and fixtures: + Mailer: app/models/notifications.rb + Views: app/views/notifications/signup.erb [...] + Test: test/unit/test/unit/notifications_test.rb + Fixtures: test/fixtures/notifications/signup [...] diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb new file mode 100644 index 00000000000..ba6d60cac61 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb @@ -0,0 +1,30 @@ +class MailerGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # Mailer, view, test, and fixture directories. + m.directory File.join('app/models', class_path) + m.directory File.join('app/views', file_path) + m.directory File.join('test/unit', class_path) + m.directory File.join('test/fixtures', file_path) + + # Mailer class and unit test. + m.template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb") + m.template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb") + + # View template and fixture for each action. + actions.each do |action| + relative_path = File.join(file_path, action) + view_path = File.join('app/views', "#{relative_path}.erb") + fixture_path = File.join('test/fixtures', relative_path) + + m.template "view.erb", view_path, + :assigns => { :action => action, :path => view_path } + m.template "fixture.erb", fixture_path, + :assigns => { :action => action, :path => view_path } + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb new file mode 100644 index 00000000000..6899257ddcb --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb @@ -0,0 +1,3 @@ +<%= class_name %>#<%= action %> + +Find me in <%= path %> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb new file mode 100644 index 00000000000..ce15ae9de90 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb @@ -0,0 +1,15 @@ +class <%= class_name %> < ActionMailer::Base + +<% for action in actions -%> + + def <%= action %>(sent_at = Time.now) + subject '<%= class_name %>#<%= action %>' + recipients '' + from '' + sent_on sent_at + + body :greeting => 'Hi,' + end +<% end -%> + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb new file mode 100644 index 00000000000..4de94076e97 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class <%= class_name %>Test < ActionMailer::TestCase +<% for action in actions -%> + test "<%= action %>" do + @expected.subject = '<%= class_name %>#<%= action %>' + @expected.body = read_fixture('<%= action %>') + @expected.date = Time.now + + assert_equal @expected.encoded, <%= class_name %>.create_<%= action %>(@expected.date).encoded + end + +<% end -%> +<% if actions.blank? -%> + # replace this with your real tests + test "the truth" do + assert true + end +<% end -%> +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/view.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/view.erb new file mode 100644 index 00000000000..6899257ddcb --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/view.erb @@ -0,0 +1,3 @@ +<%= class_name %>#<%= action %> + +Find me in <%= path %> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/USAGE new file mode 100644 index 00000000000..123ec6c03f3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/USAGE @@ -0,0 +1,8 @@ +Description: + Cast some metal! + +Examples: + `./script/generate metal poller` + + This will create: + Metal: app/metal/poller.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/metal_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/metal_generator.rb new file mode 100644 index 00000000000..64f49d929d8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/metal_generator.rb @@ -0,0 +1,8 @@ +class MetalGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + m.directory 'app/metal' + m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/templates/metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/templates/metal.rb new file mode 100644 index 00000000000..e94982b69ac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/metal/templates/metal.rb @@ -0,0 +1,12 @@ +# Allow the metal piece to run in isolation +require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails) + +class <%= class_name %> + def self.call(env) + if env["PATH_INFO"] =~ /^\/<%= file_name %>/ + [200, {"Content-Type" => "text/html"}, ["Hello, World!"]] + else + [404, {"Content-Type" => "text/html"}, ["Not Found"]] + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/USAGE new file mode 100644 index 00000000000..b83c657963b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/USAGE @@ -0,0 +1,29 @@ +Description: + Stubs out a new database migration. Pass the migration name, either + CamelCased or under_scored, and an optional list of attribute pairs as arguments. + + A migration class is generated in db/migrate prefixed by a timestamp of the current date and time. + + You can name your migration in either of these formats to generate add/remove + column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable + +Example: + `./script/generate migration AddSslFlag` + + If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration + db/migrate/20080514090912_add_ssl_flag.rb + + `./script/generate migration AddTitleBodyToPost title:string body:text published:boolean` + + This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with + this in the Up migration: + + add_column :posts, :title, :string + add_column :posts, :body, :text + add_column :posts, :published, :boolean + + And this in the Down migration: + + remove_column :posts, :published + remove_column :posts, :body + remove_column :posts, :title diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/migration_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/migration_generator.rb new file mode 100644 index 00000000000..acf41e07df5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/migration_generator.rb @@ -0,0 +1,20 @@ +class MigrationGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + m.migration_template 'migration.rb', 'db/migrate', :assigns => get_local_assigns + end + end + + + private + def get_local_assigns + returning(assigns = {}) do + if class_name.underscore =~ /^(add|remove)_.*_(?:to|from)_(.*)/ + assigns[:migration_action] = $1 + assigns[:table_name] = $2.pluralize + else + assigns[:attributes] = [] + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/templates/migration.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/templates/migration.rb new file mode 100644 index 00000000000..ca35a432294 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/migration/templates/migration.rb @@ -0,0 +1,11 @@ +class <%= class_name.underscore.camelize %> < ActiveRecord::Migration + def self.up<% attributes.each do |attribute| %> + <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%> + <%- end %> + end + + def self.down<% attributes.reverse.each do |attribute| %> + <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%> + <%- end %> + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/USAGE new file mode 100644 index 00000000000..24b03b4d4aa --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/USAGE @@ -0,0 +1,27 @@ +Description: + Stubs out a new model. Pass the model name, either CamelCased or + under_scored, and an optional list of attribute pairs as arguments. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + You don't have to think up every attribute up front, but it helps to + sketch out a few so you can start working with the model immediately. + + This generates a model class in app/models, a unit test in test/unit, + a test fixture in test/fixtures/singular_name.yml, and a migration in + db/migrate. + +Examples: + `./script/generate model account` + + creates an Account model, test, fixture, and migration: + Model: app/models/account.rb + Test: test/unit/account_test.rb + Fixtures: test/fixtures/accounts.yml + Migration: db/migrate/XXX_add_accounts.rb + + `./script/generate model post title:string body:text published:boolean` + + creates a Post model with a string title, text body, and published flag. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/model_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/model_generator.rb new file mode 100644 index 00000000000..1895d8a9c6e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/model_generator.rb @@ -0,0 +1,52 @@ +class ModelGenerator < Rails::Generator::NamedBase + default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # Model, test, and fixture directories. + m.directory File.join('app/models', class_path) + m.directory File.join('test/unit', class_path) + m.directory File.join('test/fixtures', class_path) + + # Model class, unit test, and fixtures. + m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb") + m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb") + + unless options[:skip_fixture] + m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") + end + + migration_file_path = file_path.gsub(/\//, '_') + migration_name = class_name + if ActiveRecord::Base.pluralize_table_names + migration_name = migration_name.pluralize + migration_file_path = migration_file_path.pluralize + end + + unless options[:skip_migration] + m.migration_template 'migration.rb', 'db/migrate', :assigns => { + :migration_name => "Create#{migration_name.gsub(/::/, '')}" + }, :migration_file_name => "create_#{migration_file_path}" + end + end + end + + protected + def banner + "Usage: #{$0} #{spec.name} ModelName [field:type, field:type]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-timestamps", + "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + opt.on("--skip-fixture", + "Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v} + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml new file mode 100644 index 00000000000..c21035113e5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml @@ -0,0 +1,19 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html + +<% unless attributes.empty? -%> +one: +<% for attribute in attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<% end -%> + +two: +<% for attribute in attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<% end -%> +<% else -%> +# one: +# column: value +# +# two: +# column: value +<% end -%> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/migration.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/migration.rb new file mode 100644 index 00000000000..382fd1156ee --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/migration.rb @@ -0,0 +1,16 @@ +class <%= migration_name %> < ActiveRecord::Migration + def self.up + create_table :<%= table_name %> do |t| +<% for attribute in attributes -%> + t.<%= attribute.type %> :<%= attribute.name %> +<% end -%> +<% unless options[:skip_timestamps] %> + t.timestamps +<% end -%> + end + end + + def self.down + drop_table :<%= table_name %> + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/model.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/model.rb new file mode 100644 index 00000000000..6fcf393bdf1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/model.rb @@ -0,0 +1,5 @@ +class <%= class_name %> < ActiveRecord::Base +<% attributes.select(&:reference?).each do |attribute| -%> + belongs_to :<%= attribute.name %> +<% end -%> +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb new file mode 100644 index 00000000000..3e0bc29d3ad --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/USAGE new file mode 100644 index 00000000000..a5d744a3c2a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/USAGE @@ -0,0 +1,13 @@ +Description: + Stubs out a new observer. Pass the observer name, either CamelCased or + under_scored, as an argument. + + The generator creates an observer class in app/models and a unit test in + test/unit. + +Example: + `./script/generate observer Account` + + creates an Account observer and unit test: + Observer: app/models/account_observer.rb + Test: test/unit/account_observer_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/observer_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/observer_generator.rb new file mode 100644 index 00000000000..3c4b330a801 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/observer_generator.rb @@ -0,0 +1,16 @@ +class ObserverGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions "#{class_name}Observer", "#{class_name}ObserverTest" + + # Observer, and test directories. + m.directory File.join('app/models', class_path) + m.directory File.join('test/unit', class_path) + + # Observer class and unit test fixtures. + m.template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb") + m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/templates/observer.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/templates/observer.rb new file mode 100644 index 00000000000..b9a3004161a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/templates/observer.rb @@ -0,0 +1,2 @@ +class <%= class_name %>Observer < ActiveRecord::Observer +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb new file mode 100644 index 00000000000..03f6d5666e5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>ObserverTest < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/USAGE new file mode 100644 index 00000000000..d84051eb02d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/USAGE @@ -0,0 +1,8 @@ +Description: + Stubs out a new performance test. Pass the name of the test, either + CamelCased or under_scored, as an argument. The new test class is + generated in test/performance/testname_test.rb + +Example: + `./script/generate performance_test GeneralStories` creates a GeneralStories + performance test in test/performance/general_stories_test.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb new file mode 100644 index 00000000000..83ce8ac6740 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb @@ -0,0 +1,16 @@ +class PerformanceTestGenerator < Rails::Generator::NamedBase + default_options :skip_migration => false + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name, "#{class_name}Test" + + # performance test directory + m.directory File.join('test/performance', class_path) + + # performance test stub + m.template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb") + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb new file mode 100644 index 00000000000..27c91b0fcac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' +require 'performance_test_help' + +class <%= class_name %>Test < ActionController::PerformanceTest + # Replace this with your real tests. + def test_homepage + get '/' + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/USAGE new file mode 100644 index 00000000000..d2ecfc2d59c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/USAGE @@ -0,0 +1,25 @@ +Description: + Stubs out a new plugin. Pass the plugin name, either CamelCased or + under_scored, as an argument. Pass --with-generator to add an example + generator also. + + This creates a plugin in vendor/plugins including an init.rb and README + as well as standard lib, task, and test directories. + +Example: + `./script/generate plugin BrowserFilters` + + creates a standard browser_filters plugin: + vendor/plugins/browser_filters/README + vendor/plugins/browser_filters/init.rb + vendor/plugins/browser_filters/install.rb + vendor/plugins/browser_filters/lib/browser_filters.rb + vendor/plugins/browser_filters/test/browser_filters_test.rb + vendor/plugins/browser_filters/tasks/browser_filters_tasks.rake + + ./script/generate plugin BrowserFilters --with-generator + + creates a browser_filters generator also: + vendor/plugins/browser_filters/generators/browser_filters/browser_filters_generator.rb + vendor/plugins/browser_filters/generators/browser_filters/USAGE + vendor/plugins/browser_filters/generators/browser_filters/templates/ diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb new file mode 100644 index 00000000000..6826998252f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb @@ -0,0 +1,39 @@ +class PluginGenerator < Rails::Generator::NamedBase + attr_reader :plugin_path + + def initialize(runtime_args, runtime_options = {}) + @with_generator = runtime_args.delete("--with-generator") + super + @plugin_path = "vendor/plugins/#{file_name}" + end + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_name + + m.directory "#{plugin_path}/lib" + m.directory "#{plugin_path}/tasks" + m.directory "#{plugin_path}/test" + + m.template 'README', "#{plugin_path}/README" + m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE" + m.template 'Rakefile', "#{plugin_path}/Rakefile" + m.template 'init.rb', "#{plugin_path}/init.rb" + m.template 'install.rb', "#{plugin_path}/install.rb" + m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb" + m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb" + m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake" + m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb" + m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb" + if @with_generator + m.directory "#{plugin_path}/generators" + m.directory "#{plugin_path}/generators/#{file_name}" + m.directory "#{plugin_path}/generators/#{file_name}/templates" + + m.template 'generator.rb', "#{plugin_path}/generators/#{file_name}/#{file_name}_generator.rb" + m.template 'USAGE', "#{plugin_path}/generators/#{file_name}/USAGE" + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE new file mode 100644 index 00000000000..8717df053d3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) <%= Date.today.year %> [name of plugin creator] + +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. diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/README b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/README new file mode 100644 index 00000000000..702db07cb1e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/README @@ -0,0 +1,13 @@ +<%= class_name %> +<%= "=" * class_name.size %> + +Introduction goes here. + + +Example +======= + +Example goes here. + + +Copyright (c) <%= Date.today.year %> [name of plugin creator], released under the MIT license diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile new file mode 100644 index 00000000000..85e8ff18343 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile @@ -0,0 +1,23 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the <%= file_name %> plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +desc 'Generate documentation for the <%= file_name %> plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = '<%= class_name %>' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/USAGE new file mode 100644 index 00000000000..ea9f4f12cc6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/USAGE @@ -0,0 +1,8 @@ +Description: + Explain the generator + +Example: + ./script/generate <%= file_name %> Thing + + This will create: + what/will/it/create diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb new file mode 100644 index 00000000000..3e800df6c5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb @@ -0,0 +1,8 @@ +class <%= class_name %>Generator < Rails::Generator::NamedBase + def manifest + record do |m| + # m.directory "lib" + # m.template 'README', "README" + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/init.rb new file mode 100644 index 00000000000..3c19a743c9d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/init.rb @@ -0,0 +1 @@ +# Include hook code here diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/install.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/install.rb new file mode 100644 index 00000000000..f7732d3796c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/install.rb @@ -0,0 +1 @@ +# Install hook code here diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb new file mode 100644 index 00000000000..d8d908a9591 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb @@ -0,0 +1 @@ +# <%= class_name %> diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake new file mode 100644 index 00000000000..72920a9d3a3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake @@ -0,0 +1,4 @@ +# desc "Explaining what the task does" +# task :<%= file_name %> do +# # Task goes here +# end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb new file mode 100644 index 00000000000..cf148b8b474 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb @@ -0,0 +1,3 @@ +require 'rubygems' +require 'active_support' +require 'active_support/test_case' \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb new file mode 100644 index 00000000000..97383334634 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb @@ -0,0 +1 @@ +# Uninstall hook code here diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb new file mode 100644 index 00000000000..3e0bc29d3ad --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= class_name %>Test < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/USAGE new file mode 100644 index 00000000000..e6043f1de19 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/USAGE @@ -0,0 +1,23 @@ +Description: + Stubs out a new resource including an empty model and controller suitable + for a restful, resource-oriented application. Pass the singular model name, + either CamelCased or under_scored, as the first argument, and an optional + list of attribute pairs. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + You don't have to think up every attribute up front, but it helps to + sketch out a few so you can start working with the resource immediately. + + This creates a model, controller, helper, tests and fixtures for all of them, + and the corresponding map.resources declaration in config/routes.rb + + Unlike the scaffold generator, the resource generator does not create + views or add any methods to the generated controller. + +Examples: + `./script/generate resource post` # no attributes + `./script/generate resource post title:string body:text published:boolean` + `./script/generate resource purchase order_id:integer amount:decimal` diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/resource_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/resource_generator.rb new file mode 100644 index 00000000000..4ee2fbff638 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/resource_generator.rb @@ -0,0 +1,76 @@ +class ResourceGenerator < Rails::Generator::NamedBase + default_options :skip_timestamps => false, :skip_migration => false + + attr_reader :controller_name, + :controller_class_path, + :controller_file_path, + :controller_class_nesting, + :controller_class_nesting_depth, + :controller_class_name, + :controller_singular_name, + :controller_plural_name + alias_method :controller_file_name, :controller_singular_name + alias_method :controller_table_name, :controller_plural_name + + def initialize(runtime_args, runtime_options = {}) + super + + @controller_name = @name.pluralize + + base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) + @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name) + + if @controller_class_nesting.empty? + @controller_class_name = @controller_class_name_without_nesting + else + @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" + end + end + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper") + m.class_collisions(class_name) + + # Controller, helper, views, and test directories. + m.directory(File.join('app/models', class_path)) + m.directory(File.join('app/controllers', controller_class_path)) + m.directory(File.join('app/helpers', controller_class_path)) + m.directory(File.join('app/views', controller_class_path, controller_file_name)) + m.directory(File.join('test/functional', controller_class_path)) + m.directory(File.join('test/unit', class_path)) + m.directory(File.join('test/unit/helpers', class_path)) + + m.dependency 'model', [name] + @args, :collision => :skip + + m.template( + 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") + ) + + m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) + m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) + m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) + + m.route_resources controller_file_name + end + end + + protected + def banner + "Usage: #{$0} resource ModelName [field:type, field:type]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-timestamps", + "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + end + + def model_name + class_name.demodulize + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/controller.rb new file mode 100644 index 00000000000..765a9426942 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/controller.rb @@ -0,0 +1,2 @@ +class <%= controller_class_name %>Controller < ApplicationController +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb new file mode 100644 index 00000000000..b1bb1dacbf9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class <%= controller_class_name %>ControllerTest < ActionController::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/helper.rb new file mode 100644 index 00000000000..9bd821b1b24 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= controller_class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb new file mode 100644 index 00000000000..061f64a5e35 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= controller_class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/USAGE new file mode 100644 index 00000000000..810aea16f14 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/USAGE @@ -0,0 +1,29 @@ +Description: + Scaffolds an entire resource, from model and migration to controller and + views, along with a full test suite. The resource is ready to use as a + starting point for your RESTful, resource-oriented application. + + Pass the name of the model (in singular form), either CamelCased or + under_scored, as the first argument, and an optional list of attribute + pairs. + + Attribute pairs are column_name:sql_type arguments specifying the + model's attributes. Timestamps are added by default, so you don't have to + specify them by hand as 'created_at:datetime updated_at:datetime'. + + You don't have to think up every attribute up front, but it helps to + sketch out a few so you can start working with the resource immediately. + + For example, 'scaffold post title:string body:text published:boolean' + gives you a model with those three attributes, a controller that handles + the create/show/update/destroy, forms to create and edit your posts, and + an index that lists them all, as well as a map.resources :posts + declaration in config/routes.rb. + + If you want to remove all the generated files, run + 'script/destroy scaffold ModelName'. + +Examples: + `./script/generate scaffold post` + `./script/generate scaffold post title:string body:text published:boolean` + `./script/generate scaffold purchase order_id:integer amount:decimal` diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb new file mode 100644 index 00000000000..88bc3252a06 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb @@ -0,0 +1,103 @@ +class ScaffoldGenerator < Rails::Generator::NamedBase + default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false + + attr_reader :controller_name, + :controller_class_path, + :controller_file_path, + :controller_class_nesting, + :controller_class_nesting_depth, + :controller_class_name, + :controller_underscore_name, + :controller_singular_name, + :controller_plural_name + alias_method :controller_file_name, :controller_underscore_name + alias_method :controller_table_name, :controller_plural_name + + def initialize(runtime_args, runtime_options = {}) + super + + if @name == @name.pluralize && !options[:force_plural] + logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural." + @name = @name.singularize + assign_names!(@name) + end + + @controller_name = @name.pluralize + + base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) + @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name) + @controller_singular_name=base_name.singularize + if @controller_class_nesting.empty? + @controller_class_name = @controller_class_name_without_nesting + else + @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" + end + end + + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper") + m.class_collisions(class_name) + + # Controller, helper, views, test and stylesheets directories. + m.directory(File.join('app/models', class_path)) + m.directory(File.join('app/controllers', controller_class_path)) + m.directory(File.join('app/helpers', controller_class_path)) + m.directory(File.join('app/views', controller_class_path, controller_file_name)) + m.directory(File.join('app/views/layouts', controller_class_path)) + m.directory(File.join('test/functional', controller_class_path)) + m.directory(File.join('test/unit', class_path)) + m.directory(File.join('test/unit/helpers', class_path)) + m.directory(File.join('public/stylesheets', class_path)) + + for action in scaffold_views + m.template( + "view_#{action}.html.erb", + File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb") + ) + end + + # Layout and stylesheet. + m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb")) + m.template('style.css', 'public/stylesheets/scaffold.css') + + m.template( + 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") + ) + + m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) + m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) + m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) + + m.route_resources controller_file_name + + m.dependency 'model', [name] + @args, :collision => :skip + end + end + + protected + # Override with your own usage banner. + def banner + "Usage: #{$0} scaffold ModelName [field:type, field:type]" + end + + def add_options!(opt) + opt.separator '' + opt.separator 'Options:' + opt.on("--skip-timestamps", + "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } + opt.on("--skip-migration", + "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } + opt.on("--force-plural", + "Forces the generation of a plural ModelName") { |v| options[:force_plural] = v } + end + + def scaffold_views + %w[ index show new edit ] + end + + def model_name + class_name.demodulize + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb new file mode 100644 index 00000000000..4d190b9362d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb @@ -0,0 +1,85 @@ +class <%= controller_class_name %>Controller < ApplicationController + # GET /<%= table_name %> + # GET /<%= table_name %>.xml + def index + @<%= table_name %> = <%= class_name %>.all + + respond_to do |format| + format.html # index.html.erb + format.xml { render :xml => @<%= table_name %> } + end + end + + # GET /<%= table_name %>/1 + # GET /<%= table_name %>/1.xml + def show + @<%= file_name %> = <%= class_name %>.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @<%= file_name %> } + end + end + + # GET /<%= table_name %>/new + # GET /<%= table_name %>/new.xml + def new + @<%= file_name %> = <%= class_name %>.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @<%= file_name %> } + end + end + + # GET /<%= table_name %>/1/edit + def edit + @<%= file_name %> = <%= class_name %>.find(params[:id]) + end + + # POST /<%= table_name %> + # POST /<%= table_name %>.xml + def create + @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>]) + + respond_to do |format| + if @<%= file_name %>.save + flash[:notice] = '<%= class_name %> was successfully created.' + format.html { redirect_to(@<%= file_name %>) } + format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> } + else + format.html { render :action => "new" } + format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /<%= table_name %>/1 + # PUT /<%= table_name %>/1.xml + def update + @<%= file_name %> = <%= class_name %>.find(params[:id]) + + respond_to do |format| + if @<%= file_name %>.update_attributes(params[:<%= file_name %>]) + flash[:notice] = '<%= class_name %> was successfully updated.' + format.html { redirect_to(@<%= file_name %>) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /<%= table_name %>/1 + # DELETE /<%= table_name %>/1.xml + def destroy + @<%= file_name %> = <%= class_name %>.find(params[:id]) + @<%= file_name %>.destroy + + respond_to do |format| + format.html { redirect_to(<%= table_name %>_url) } + format.xml { head :ok } + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb new file mode 100644 index 00000000000..cd2fc578bf0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb @@ -0,0 +1,45 @@ +require 'test_helper' + +class <%= controller_class_name %>ControllerTest < ActionController::TestCase + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:<%= table_name %>) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create <%= file_name %>" do + assert_difference('<%= class_name %>.count') do + post :create, :<%= file_name %> => { } + end + + assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) + end + + test "should show <%= file_name %>" do + get :show, :id => <%= table_name %>(:one).to_param + assert_response :success + end + + test "should get edit" do + get :edit, :id => <%= table_name %>(:one).to_param + assert_response :success + end + + test "should update <%= file_name %>" do + put :update, :id => <%= table_name %>(:one).to_param, :<%= file_name %> => { } + assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>)) + end + + test "should destroy <%= file_name %>" do + assert_difference('<%= class_name %>.count', -1) do + delete :destroy, :id => <%= table_name %>(:one).to_param + end + + assert_redirected_to <%= table_name %>_path + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb new file mode 100644 index 00000000000..9bd821b1b24 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= controller_class_name %>Helper +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb new file mode 100644 index 00000000000..061f64a5e35 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= controller_class_name %>HelperTest < ActionView::TestCase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb new file mode 100644 index 00000000000..ebc97f8130c --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb @@ -0,0 +1,17 @@ + + + + + + <%= controller_class_name %>: <%%= controller.action_name %> + <%%= stylesheet_link_tag 'scaffold' %> + + + +

        <%%= flash[:notice] %>

        + +<%%= yield %> + + + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/style.css b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/style.css new file mode 100644 index 00000000000..093c20994d7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/style.css @@ -0,0 +1,54 @@ +body { background-color: #fff; color: #333; } + +body, p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { color: #000; } +a:visited { color: #666; } +a:hover { color: #fff; background-color:#000; } + +.fieldWithErrors { + padding: 2px; + background-color: red; + display: table; +} + +#errorExplanation { + width: 400px; + border: 2px solid red; + padding: 7px; + padding-bottom: 12px; + margin-bottom: 20px; + background-color: #f0f0f0; +} + +#errorExplanation h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + background-color: #c00; + color: #fff; +} + +#errorExplanation p { + color: #333; + margin-bottom: 0; + padding: 5px; +} + +#errorExplanation ul li { + font-size: 12px; + list-style: square; +} + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb new file mode 100644 index 00000000000..cca1d61c687 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb @@ -0,0 +1,18 @@ +

        Editing <%= singular_name %>

        + +<%% form_for(@<%= singular_name %>) do |f| %> + <%%= f.error_messages %> + +<% for attribute in attributes -%> +

        + <%%= f.label :<%= attribute.name %> %>
        + <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> +

        +<% end -%> +

        + <%%= f.submit 'Update' %> +

        +<%% end %> + +<%%= link_to 'Show', @<%= singular_name %> %> | +<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb new file mode 100644 index 00000000000..2e603d5b4a3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb @@ -0,0 +1,24 @@ +

        Listing <%= plural_name %>

        + + + +<% for attribute in attributes -%> + +<% end -%> + + +<%% @<%= plural_name %>.each do |<%= singular_name %>| %> + +<% for attribute in attributes -%> + +<% end -%> + + + + +<%% end %> +
        <%= attribute.column.human_name %>
        <%%=h <%= singular_name %>.<%= attribute.name %> %><%%= link_to 'Show', <%= singular_name %> %><%%= link_to 'Edit', edit_<%= singular_name %>_path(<%= singular_name %>) %><%%= link_to 'Destroy', <%= singular_name %>, :confirm => 'Are you sure?', :method => :delete %>
        + +
        + +<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb new file mode 100644 index 00000000000..96c89fc50ed --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb @@ -0,0 +1,17 @@ +

        New <%= singular_name %>

        + +<%% form_for(@<%= singular_name %>) do |f| %> + <%%= f.error_messages %> + +<% for attribute in attributes -%> +

        + <%%= f.label :<%= attribute.name %> %>
        + <%%= f.<%= attribute.field_type %> :<%= attribute.name %> %> +

        +<% end -%> +

        + <%%= f.submit 'Create' %> +

        +<%% end %> + +<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb new file mode 100644 index 00000000000..adecaf70c62 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb @@ -0,0 +1,10 @@ +<% for attribute in attributes -%> +

        + <%= attribute.column.human_name %>: + <%%=h @<%= singular_name %>.<%= attribute.name %> %> +

        + +<% end -%> + +<%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> | +<%%= link_to 'Back', <%= plural_name %>_path %> \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/USAGE b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/USAGE new file mode 100644 index 00000000000..87117a3cb66 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/USAGE @@ -0,0 +1,10 @@ +Description: + Creates a migration to add the sessions table used by the Active Record + session store. Pass the migration name, either CamelCased or under_scored, + as an argument. + +Example: + `./script/generate session_migration CreateSessionTable` + + With 4 existing migrations, this creates the AddSessionTable migration + in db/migrate/005_add_session_table.rb diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb new file mode 100644 index 00000000000..2e177033a18 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb @@ -0,0 +1,18 @@ +class SessionMigrationGenerator < Rails::Generator::NamedBase + def initialize(runtime_args, runtime_options = {}) + runtime_args << 'add_session_table' if runtime_args.empty? + super + end + + def manifest + record do |m| + m.migration_template 'migration.rb', 'db/migrate', + :assigns => { :session_table_name => default_session_table_name } + end + end + + protected + def default_session_table_name + ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session' + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb new file mode 100644 index 00000000000..ca220a5f233 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb @@ -0,0 +1,16 @@ +class <%= class_name %> < ActiveRecord::Migration + def self.up + create_table :<%= session_table_name %> do |t| + t.string :session_id, :null => false + t.text :data + t.timestamps + end + + add_index :<%= session_table_name %>, :session_id + add_index :<%= session_table_name %>, :updated_at + end + + def self.down + drop_table :<%= session_table_name %> + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/lookup.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/lookup.rb new file mode 100644 index 00000000000..a3525364a2f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/lookup.rb @@ -0,0 +1,249 @@ +require 'pathname' + +require File.dirname(__FILE__) + '/spec' + +class Object + class << self + # Lookup missing generators using const_missing. This allows any + # generator to reference another without having to know its location: + # RubyGems, ~/.rails/generators, and RAILS_ROOT/generators. + def lookup_missing_generator(class_id) + if md = /(.+)Generator$/.match(class_id.to_s) + name = md.captures.first.demodulize.underscore + Rails::Generator::Base.lookup(name).klass + else + const_missing_before_generators(class_id) + end + end + + unless respond_to?(:const_missing_before_generators) + alias_method :const_missing_before_generators, :const_missing + alias_method :const_missing, :lookup_missing_generator + end + end +end + +# User home directory lookup adapted from RubyGems. +def Dir.user_home + if ENV['HOME'] + ENV['HOME'] + elsif ENV['USERPROFILE'] + ENV['USERPROFILE'] + elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH'] + "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" + else + File.expand_path '~' + end +end + + +module Rails + module Generator + + # Generator lookup is managed by a list of sources which return specs + # describing where to find and how to create generators. This module + # provides class methods for manipulating the source list and looking up + # generator specs, and an #instance wrapper for quickly instantiating + # generators by name. + # + # A spec is not a generator: it's a description of where to find + # the generator and how to create it. A source is anything that + # yields generators from #each. PathSource and GemGeneratorSource are provided. + module Lookup + def self.included(base) + base.extend(ClassMethods) + base.use_component_sources! + end + + # Convenience method to instantiate another generator. + def instance(generator_name, args, runtime_options = {}) + self.class.instance(generator_name, args, runtime_options) + end + + module ClassMethods + # The list of sources where we look, in order, for generators. + def sources + read_inheritable_attribute(:sources) or use_component_sources! + end + + # Add a source to the end of the list. + def append_sources(*args) + sources.concat(args.flatten) + invalidate_cache! + end + + # Add a source to the beginning of the list. + def prepend_sources(*args) + write_inheritable_array(:sources, args.flatten + sources) + invalidate_cache! + end + + # Reset the source list. + def reset_sources + write_inheritable_attribute(:sources, []) + invalidate_cache! + end + + # Use application generators (app, ?). + def use_application_sources! + reset_sources + sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications") + end + + # Use component generators (model, controller, etc). + # 1. Rails application. If RAILS_ROOT is defined we know we're + # generating in the context of a Rails application, so search + # RAILS_ROOT/generators. + # 2. Look in plugins, either for generators/ or rails_generators/ + # directories within each plugin + # 3. User home directory. Search ~/.rails/generators. + # 4. RubyGems. Search for gems named *_generator, and look for + # generators within any RubyGem's + # /rails_generators/_generator.rb file. + # 5. Builtins. Model, controller, mailer, scaffold, and so on. + def use_component_sources! + reset_sources + if defined? ::RAILS_ROOT + sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators") + sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators") + Rails.configuration.plugin_paths.each do |path| + relative_path = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(::RAILS_ROOT)) + sources << PathSource.new(:"plugins (#{relative_path})", "#{path}/*/**/{,rails_}generators") + end + end + sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators") + if Object.const_defined?(:Gem) + sources << GemGeneratorSource.new + sources << GemPathSource.new + end + sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components") + end + + # Lookup knows how to find generators' Specs from a list of Sources. + # Searches the sources, in order, for the first matching name. + def lookup(generator_name) + @found ||= {} + generator_name = generator_name.to_s.downcase + @found[generator_name] ||= cache.find { |spec| spec.name == generator_name } + unless @found[generator_name] + chars = generator_name.scan(/./).map{|c|"#{c}.*?"} + rx = /^#{chars}$/ + gns = cache.select{|spec| spec.name =~ rx } + @found[generator_name] ||= gns.first if gns.length == 1 + raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1 + end + @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator" + end + + # Convenience method to lookup and instantiate a generator. + def instance(generator_name, args = [], runtime_options = {}) + lookup(generator_name).klass.new(args, full_options(runtime_options)) + end + + private + # Lookup and cache every generator from the source list. + def cache + @cache ||= sources.inject([]) { |cache, source| cache + source.to_a } + end + + # Clear the cache whenever the source list changes. + def invalidate_cache! + @cache = nil + end + end + end + + # Sources enumerate (yield from #each) generator specs which describe + # where to find and how to create generators. Enumerable is mixed in so, + # for example, source.collect will retrieve every generator. + # Sources may be assigned a label to distinguish them. + class Source + include Enumerable + + attr_reader :label + def initialize(label) + @label = label + end + + # The each method must be implemented in subclasses. + # The base implementation raises an error. + def each + raise NotImplementedError + end + + # Return a convenient sorted list of all generator names. + def names + map { |spec| spec.name }.sort + end + end + + + # PathSource looks for generators in a filesystem directory. + class PathSource < Source + attr_reader :path + + def initialize(label, path) + super label + @path = path + end + + # Yield each eligible subdirectory. + def each + Dir["#{path}/[a-z]*"].each do |dir| + if File.directory?(dir) + yield Spec.new(File.basename(dir), dir, label) + end + end + end + end + + class AbstractGemSource < Source + def initialize + super :RubyGems + end + end + + # GemGeneratorSource hits the mines to quarry for generators. The latest versions + # of gems named *_generator are selected. + class GemGeneratorSource < AbstractGemSource + # Yield latest versions of generator gems. + def each + dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default) + Gem::cache.search(dependency).inject({}) { |latest, gem| + hem = latest[gem.name] + latest[gem.name] = gem if hem.nil? or gem.version > hem.version + latest + }.values.each { |gem| + yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label) + } + end + end + + # GemPathSource looks for generators within any RubyGem's /rails_generators/_generator.rb file. + class GemPathSource < AbstractGemSource + # Yield each generator within rails_generator subdirectories. + def each + generator_full_paths.each do |generator| + yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label) + end + end + + private + def generator_full_paths + @generator_full_paths ||= + Gem::cache.inject({}) do |latest, name_gem| + name, gem = name_gem + hem = latest[gem.name] + latest[gem.name] = gem if hem.nil? or gem.version > hem.version + latest + end.values.inject([]) do |mem, gem| + Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator| + mem << generator + end + mem + end + end + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/manifest.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/manifest.rb new file mode 100644 index 00000000000..702effa76f5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/manifest.rb @@ -0,0 +1,53 @@ +module Rails + module Generator + + # Manifest captures the actions a generator performs. Instantiate + # a manifest with an optional target object, hammer it with actions, + # then replay or rewind on the object of your choice. + # + # Example: + # manifest = Manifest.new { |m| + # m.make_directory '/foo' + # m.create_file '/foo/bar.txt' + # } + # manifest.replay(creator) + # manifest.rewind(destroyer) + class Manifest + attr_reader :target + + # Take a default action target. Yield self if block given. + def initialize(target = nil) + @target, @actions = target, [] + yield self if block_given? + end + + # Record an action. + def method_missing(action, *args, &block) + @actions << [action, args, block] + end + + # Replay recorded actions. + def replay(target = nil) + send_actions(target || @target, @actions) + end + + # Rewind recorded actions. + def rewind(target = nil) + send_actions(target || @target, @actions.reverse) + end + + # Erase recorded actions. + def erase + @actions = [] + end + + private + def send_actions(target, actions) + actions.each do |method, args, block| + target.send(method, *args, &block) + end + end + end + + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/options.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/options.rb new file mode 100644 index 00000000000..070f96b69b9 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/options.rb @@ -0,0 +1,150 @@ +require 'optparse' + +module Rails + module Generator + module Options + def self.included(base) + base.extend(ClassMethods) + class << base + if respond_to?(:inherited) + alias_method :inherited_without_options, :inherited + end + alias_method :inherited, :inherited_with_options + end + end + + module ClassMethods + def inherited_with_options(sub) + inherited_without_options(sub) if respond_to?(:inherited_without_options) + sub.extend(Rails::Generator::Options::ClassMethods) + end + + def mandatory_options(options = nil) + if options + write_inheritable_attribute(:mandatory_options, options) + else + read_inheritable_attribute(:mandatory_options) or write_inheritable_attribute(:mandatory_options, {}) + end + end + + def default_options(options = nil) + if options + write_inheritable_attribute(:default_options, options) + else + read_inheritable_attribute(:default_options) or write_inheritable_attribute(:default_options, {}) + end + end + + # Merge together our class options. In increasing precedence: + # default_options (class default options) + # runtime_options (provided as argument) + # mandatory_options (class mandatory options) + def full_options(runtime_options = {}) + default_options.merge(runtime_options).merge(mandatory_options) + end + + end + + # Each instance has an options hash that's populated by #parse. + def options + @options ||= {} + end + attr_writer :options + + protected + # Convenient access to class mandatory options. + def mandatory_options + self.class.mandatory_options + end + + # Convenient access to class default options. + def default_options + self.class.default_options + end + + # Merge together our instance options. In increasing precedence: + # default_options (class default options) + # options (instance options) + # runtime_options (provided as argument) + # mandatory_options (class mandatory options) + def full_options(runtime_options = {}) + self.class.full_options(options.merge(runtime_options)) + end + + # Parse arguments into the options hash. Classes may customize + # parsing behavior by overriding these methods: + # #banner Usage: ./script/generate [options] + # #add_options! Options: + # some options.. + # #add_general_options! General Options: + # general options.. + def parse!(args, runtime_options = {}) + self.options = {} + + @option_parser = OptionParser.new do |opt| + opt.banner = banner + add_options!(opt) + add_general_options!(opt) + opt.parse!(args) + end + + return args + ensure + self.options = full_options(runtime_options) + end + + # Raise a usage error. Override usage_message to provide a blurb + # after the option parser summary. + def usage(message = usage_message) + raise UsageError, "#{@option_parser}\n#{message}" + end + + def usage_message + '' + end + + # Override with your own usage banner. + def banner + "Usage: #{$0} [options]" + end + + # Override to add your options to the parser: + # def add_options!(opt) + # opt.on('-v', '--verbose') { |value| options[:verbose] = value } + # end + def add_options!(opt) + end + + # Adds general options like -h and --quiet. Usually don't override. + def add_general_options!(opt) + opt.separator '' + opt.separator 'Rails Info:' + opt.on('-v', '--version', 'Show the Rails version number and quit.') + opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v } + + opt.separator '' + opt.separator 'General Options:' + + opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v } + opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force } + opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip } + opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v } + opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v } + opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do + options[:svn] = {} + `svn status`.each_line do |line| + options[:svn][line.chomp[7..-1]] = true + end + end + opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do + options[:git] = {:new => {}, :modified => {}} + `git status`.each_line do |line| + options[:git][:new][line.chomp[14..-1]] = true if line =~ /new file:/ + options[:git][:modified][line.chomp[14..-1]] = true if line =~ /modified:/ + end + end + end + + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/scripts.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/scripts.rb new file mode 100644 index 00000000000..9b1a99838a6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/scripts.rb @@ -0,0 +1,89 @@ +require File.dirname(__FILE__) + '/options' + +module Rails + module Generator + module Scripts + + # Generator scripts handle command-line invocation. Each script + # responds to an invoke! class method which handles option parsing + # and generator invocation. + class Base + include Options + default_options :collision => :ask, :quiet => false + + # Run the generator script. Takes an array of unparsed arguments + # and a hash of parsed arguments, takes the generator as an option + # or first remaining argument, and invokes the requested command. + def run(args = [], runtime_options = {}) + begin + parse!(args.dup, runtime_options) + rescue OptionParser::InvalidOption => e + # Don't cry, script. Generators want what you think is invalid. + end + + # Generator name is the only required option. + unless options[:generator] + usage if args.empty? + options[:generator] ||= args.shift + end + + # Look up generator instance and invoke command on it. + Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke! + rescue => e + puts e + puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace] + raise SystemExit + end + + protected + # Override with your own script usage banner. + def banner + "Usage: #{$0} generator [options] [args]" + end + + def usage_message + usage = "\nInstalled Generators\n" + Rails::Generator::Base.sources.inject([]) do |mem, source| + # Using an association list instead of a hash to preserve order, + # for aesthetic reasons more than anything else. + label = source.label.to_s.capitalize + pair = mem.assoc(label) + mem << (pair = [label, []]) if pair.nil? + pair[1] |= source.names + mem + end.each do |label, names| + usage << " #{label}: #{names.join(', ')}\n" unless names.empty? + end + + usage << < :destroy + + protected + def usage_message + usage = "\nInstalled Generators\n" + Rails::Generator::Base.sources.each do |source| + label = source.label.to_s.capitalize + names = source.names + usage << " #{label}: #{names.join(', ')}\n" unless names.empty? + end + + usage << < :create + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/scripts/update.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/scripts/update.rb new file mode 100644 index 00000000000..53a9faa3663 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/scripts/update.rb @@ -0,0 +1,12 @@ +require File.dirname(__FILE__) + '/../scripts' + +module Rails::Generator::Scripts + class Update < Base + mandatory_options :command => :update + + protected + def banner + "Usage: #{$0} [options] scaffold" + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/secret_key_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/secret_key_generator.rb new file mode 100644 index 00000000000..7dd495a2f50 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/secret_key_generator.rb @@ -0,0 +1,24 @@ +require 'active_support/deprecation' + +module Rails + # A class for creating random secret keys. This class will do its best to create a + # random secret key that's as secure as possible, using whatever methods are + # available on the current platform. For example: + # + # generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name") + # generator.generate_secret # => "f3f1be90053fa851... (some long string)" + # + # This class is *deprecated* in Rails 2.2 in favor of ActiveSupport::SecureRandom. + # It is currently a wrapper around ActiveSupport::SecureRandom. + class SecretKeyGenerator + def initialize(identifier) + end + + # Generate a random secret key with the best possible method available on + # the current platform. + def generate_secret + ActiveSupport::SecureRandom.hex(64) + end + deprecate :generate_secret=>"You should use ActiveSupport::SecureRandom.hex(64)" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/simple_logger.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/simple_logger.rb new file mode 100644 index 00000000000..d750f07b84f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/simple_logger.rb @@ -0,0 +1,46 @@ +module Rails + module Generator + class SimpleLogger # :nodoc: + attr_reader :out + attr_accessor :quiet + + def initialize(out = $stdout) + @out = out + @quiet = false + @level = 0 + end + + def log(status, message, &block) + @out.print("%12s %s%s\n" % [status, ' ' * @level, message]) unless quiet + indent(&block) if block_given? + end + + def indent(&block) + @level += 1 + if block_given? + begin + block.call + ensure + outdent + end + end + end + + def outdent + @level -= 1 + if block_given? + begin + block.call + ensure + indent + end + end + end + + private + def method_missing(method, *args, &block) + log(method.to_s, args.first, &block) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/spec.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/spec.rb new file mode 100644 index 00000000000..9d780b7ac5f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rails_generator/spec.rb @@ -0,0 +1,44 @@ +module Rails + module Generator + # A spec knows where a generator was found and how to instantiate it. + # Metadata include the generator's name, its base path, and the source + # which yielded it (PathSource, GemPathSource, etc.) + class Spec + attr_reader :name, :path, :source + + def initialize(name, path, source) + @name, @path, @source = name, path, source + end + + # Look up the generator class. Require its class file, find the class + # in ObjectSpace, tag it with this spec, and return. + def klass + unless @klass + require class_file + @klass = lookup_class + @klass.spec = self + end + @klass + end + + def class_file + "#{path}/#{name}_generator.rb" + end + + def class_name + "#{name.camelize}Generator" + end + + private + # Search for the first Class descending from Rails::Generator::Base + # whose name matches the requested class name. + def lookup_class + ObjectSpace.each_object(Class) do |obj| + return obj if obj.ancestors.include?(Rails::Generator::Base) and + obj.name.split('::').last == class_name + end + raise NameError, "Missing #{class_name} class in #{class_file}" + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/railties_path.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/railties_path.rb new file mode 100644 index 00000000000..a298a4cc275 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/railties_path.rb @@ -0,0 +1 @@ +RAILTIES_PATH = File.join(File.dirname(__FILE__), '..') diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/ruby_version_check.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/ruby_version_check.rb new file mode 100644 index 00000000000..68d3acc8764 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/ruby_version_check.rb @@ -0,0 +1,17 @@ +min_release = "1.8.2 (2004-12-25)" +ruby_release = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" +if ruby_release =~ /1\.8\.3/ + abort <<-end_message + + Rails does not work with Ruby version 1.8.3. + Please upgrade to version 1.8.4 or downgrade to 1.8.2. + + end_message +elsif ruby_release < min_release + abort <<-end_message + + Rails requires Ruby version #{min_release} or later. + You're running #{ruby_release}; please upgrade to continue. + + end_message +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rubyprof_ext.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rubyprof_ext.rb new file mode 100644 index 00000000000..f6e90357cee --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/rubyprof_ext.rb @@ -0,0 +1,35 @@ +require 'prof' + +module Prof #:nodoc: + # Adapted from Shugo Maeda's unprof.rb + def self.print_profile(results, io = $stderr) + total = results.detect { |i| + i.method_class.nil? && i.method_id == :"#toplevel" + }.total_time + total = 0.001 if total < 0.001 + + io.puts " %% cumulative self self total" + io.puts " time seconds seconds calls ms/call ms/call name" + + sum = 0.0 + for r in results + sum += r.self_time + + name = if r.method_class.nil? + r.method_id.to_s + elsif r.method_class.is_a?(Class) + "#{r.method_class}##{r.method_id}" + else + "#{r.method_class}.#{r.method_id}" + end + io.printf "%6.2f %8.3f %8.3f %8d %8.2f %8.2f %s\n", + r.self_time / total * 100, + sum, + r.self_time, + r.count, + r.self_time * 1000 / r.count, + r.total_time * 1000 / r.count, + name + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/source_annotation_extractor.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/source_annotation_extractor.rb new file mode 100644 index 00000000000..591fd6f6bd3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/source_annotation_extractor.rb @@ -0,0 +1,102 @@ +# Implements the logic behind the rake tasks for annotations like +# +# rake notes +# rake notes:optimize +# +# and friends. See rake -T notes and railties/lib/tasks/annotations.rake. +# +# Annotation objects are triplets :line, :tag, :text that +# represent the line where the annotation lives, its tag, and its text. Note +# the filename is not stored. +# +# Annotations are looked for in comments and modulus whitespace they have to +# start with the tag optionally followed by a colon. Everything up to the end +# of the line (or closing ERb comment tag) is considered to be their text. +class SourceAnnotationExtractor + class Annotation < Struct.new(:line, :tag, :text) + + # Returns a representation of the annotation that looks like this: + # + # [126] [TODO] This algorithm is simple and clearly correct, make it faster. + # + # If +options+ has a flag :tag the tag is shown as in the example above. + # Otherwise the string contains just line and text. + def to_s(options={}) + s = "[%3d] " % line + s << "[#{tag}] " if options[:tag] + s << text + end + end + + # Prints all annotations with tag +tag+ under the root directories +app+, +lib+, + # and +test+ (recursively). Only filenames with extension +.builder+, +.rb+, + # +.rxml+, +.rjs+, +.rhtml+, or +.erb+ are taken into account. The +options+ + # hash is passed to each annotation's +to_s+. + # + # This class method is the single entry point for the rake tasks. + def self.enumerate(tag, options={}) + extractor = new(tag) + extractor.display(extractor.find, options) + end + + attr_reader :tag + + def initialize(tag) + @tag = tag + end + + # Returns a hash that maps filenames under +dirs+ (recursively) to arrays + # with their annotations. Only files with annotations are included, and only + # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+ + # are taken into account. + def find(dirs=%w(app lib test)) + dirs.inject({}) { |h, dir| h.update(find_in(dir)) } + end + + # Returns a hash that maps filenames under +dir+ (recursively) to arrays + # with their annotations. Only files with annotations are included, and only + # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+ + # are taken into account. + def find_in(dir) + results = {} + + Dir.glob("#{dir}/*") do |item| + next if File.basename(item)[0] == ?. + + if File.directory?(item) + results.update(find_in(item)) + elsif item =~ /\.(builder|(r(?:b|xml|js)))$/ + results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/)) + elsif item =~ /\.(rhtml|erb)$/ + results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/)) + end + end + + results + end + + # If +file+ is the filename of a file that contains annotations this method returns + # a hash with a single entry that maps +file+ to an array of its annotations. + # Otherwise it returns an empty hash. + def extract_annotations_from(file, pattern) + lineno = 0 + result = File.readlines(file).inject([]) do |list, line| + lineno += 1 + next list unless line =~ pattern + list << Annotation.new(lineno, $1, $2) + end + result.empty? ? {} : { file => result } + end + + # Prints the mapping from filenames to annotations in +results+ ordered by filename. + # The +options+ hash is passed to each annotation's +to_s+. + def display(results, options={}) + results.keys.sort.each do |file| + puts "#{file}:" + results[file].each do |note| + puts " * #{note.to_s(options)}" + end + puts + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/annotations.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/annotations.rake new file mode 100644 index 00000000000..48ac40099a0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/annotations.rake @@ -0,0 +1,20 @@ +require 'source_annotation_extractor' + +desc "Enumerate all annotations" +task :notes do + SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true +end + +namespace :notes do + ["OPTIMIZE", "FIXME", "TODO"].each do |annotation| + desc "Enumerate all #{annotation} annotations" + task annotation.downcase.intern do + SourceAnnotationExtractor.enumerate annotation + end + end + + desc "Enumerate a custom annotation, specify with ANNOTATION=WTFHAX" + task :custom do + SourceAnnotationExtractor.enumerate ENV['ANNOTATION'] + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/databases.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/databases.rake new file mode 100644 index 00000000000..8b608396417 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/databases.rake @@ -0,0 +1,436 @@ +namespace :db do + task :load_config => :rails_env do + require 'active_record' + ActiveRecord::Base.configurations = Rails::Configuration.new.database_configuration + end + + namespace :create do + desc 'Create all the local databases defined in config/database.yml' + task :all => :load_config do + ActiveRecord::Base.configurations.each_value do |config| + # Skip entries that don't have a database key, such as the first entry here: + # + # defaults: &defaults + # adapter: mysql + # username: root + # password: + # host: localhost + # + # development: + # database: blog_development + # <<: *defaults + next unless config['database'] + # Only connect to local databases + local_database?(config) { create_database(config) } + end + end + end + + desc 'Create the database defined in config/database.yml for the current RAILS_ENV' + task :create => :load_config do + create_database(ActiveRecord::Base.configurations[RAILS_ENV]) + end + + def create_database(config) + begin + if config['adapter'] =~ /sqlite/ + if File.exist?(config['database']) + $stderr.puts "#{config['database']} already exists" + else + begin + # Create the SQLite database + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection + rescue + $stderr.puts $!, *($!.backtrace) + $stderr.puts "Couldn't create database for #{config.inspect}" + end + end + return # Skip the else clause of begin/rescue + else + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection + end + rescue + case config['adapter'] + when 'mysql' + @charset = ENV['CHARSET'] || 'utf8' + @collation = ENV['COLLATION'] || 'utf8_unicode_ci' + begin + ActiveRecord::Base.establish_connection(config.merge('database' => nil)) + ActiveRecord::Base.connection.create_database(config['database'], :charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)) + ActiveRecord::Base.establish_connection(config) + rescue + $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation} (if you set the charset manually, make sure you have a matching collation)" + end + when 'postgresql' + @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' + begin + ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) + ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding)) + ActiveRecord::Base.establish_connection(config) + rescue + $stderr.puts $!, *($!.backtrace) + $stderr.puts "Couldn't create database for #{config.inspect}" + end + end + else + $stderr.puts "#{config['database']} already exists" + end + end + + namespace :drop do + desc 'Drops all the local databases defined in config/database.yml' + task :all => :load_config do + ActiveRecord::Base.configurations.each_value do |config| + # Skip entries that don't have a database key + next unless config['database'] + # Only connect to local databases + local_database?(config) { drop_database(config) } + end + end + end + + desc 'Drops the database for the current RAILS_ENV' + task :drop => :load_config do + config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + begin + drop_database(config) + rescue Exception => e + puts "Couldn't drop #{config['database']} : #{e.inspect}" + end + end + + def local_database?(config, &block) + if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank? + yield + else + puts "This task only modifies local databases. #{config['database']} is on a remote host." + end + end + + + desc "Migrate the database through scripts in db/migrate and update db/schema.rb by invoking db:schema:dump. Target specific version with VERSION=x. Turn off output with VERBOSE=false." + task :migrate => :environment do + ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true + ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + namespace :migrate do + desc 'Rollbacks the database one migration and re migrate up. If you want to rollback more than one step, define STEP=x. Target specific version with VERSION=x.' + task :redo => :environment do + if ENV["VERSION"] + Rake::Task["db:migrate:down"].invoke + Rake::Task["db:migrate:up"].invoke + else + Rake::Task["db:rollback"].invoke + Rake::Task["db:migrate"].invoke + end + end + + desc 'Resets your database using your migrations for the current environment' + task :reset => ["db:drop", "db:create", "db:migrate"] + + desc 'Runs the "up" for a given migration VERSION.' + task :up => :environment do + version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil + raise "VERSION is required" unless version + ActiveRecord::Migrator.run(:up, "db/migrate/", version) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + desc 'Runs the "down" for a given migration VERSION.' + task :down => :environment do + version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil + raise "VERSION is required" unless version + ActiveRecord::Migrator.run(:down, "db/migrate/", version) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + end + + desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n' + task :rollback => :environment do + step = ENV['STEP'] ? ENV['STEP'].to_i : 1 + ActiveRecord::Migrator.rollback('db/migrate/', step) + Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + end + + desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' + task :reset => [ 'db:drop', 'db:setup' ] + + desc "Retrieves the charset for the current environment's database" + task :charset => :environment do + config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + puts ActiveRecord::Base.connection.charset + when 'postgresql' + ActiveRecord::Base.establish_connection(config) + puts ActiveRecord::Base.connection.encoding + else + puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' + end + end + + desc "Retrieves the collation for the current environment's database" + task :collation => :environment do + config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + puts ActiveRecord::Base.connection.collation + else + puts 'sorry, your database adapter is not supported yet, feel free to submit a patch' + end + end + + desc "Retrieves the current schema version number" + task :version => :environment do + puts "Current version: #{ActiveRecord::Migrator.current_version}" + end + + desc "Raises an error if there are pending migrations" + task :abort_if_pending_migrations => :environment do + if defined? ActiveRecord + pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations + + if pending_migrations.any? + puts "You have #{pending_migrations.size} pending migrations:" + pending_migrations.each do |pending_migration| + puts ' %4d %s' % [pending_migration.version, pending_migration.name] + end + abort %{Run "rake db:migrate" to update your database then try again.} + end + end + end + + desc 'Create the database, load the schema, and initialize with the seed data' + task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] + + desc 'Load the seed data from db/seeds.rb' + task :seed => :environment do + seed_file = File.join(Rails.root, 'db', 'seeds.rb') + load(seed_file) if File.exist?(seed_file) + end + + namespace :fixtures do + desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." + task :load => :environment do + require 'active_record/fixtures' + ActiveRecord::Base.establish_connection(Rails.env) + base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') + fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir + + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file| + Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*')) + end + end + + desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." + task :identify => :environment do + require "active_record/fixtures" + + label, id = ENV["LABEL"], ENV["ID"] + raise "LABEL or ID required" if label.blank? && id.blank? + + puts %Q(The fixture ID for "#{label}" is #{Fixtures.identify(label)}.) if label + + base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') + Dir["#{base_dir}/**/*.yml"].each do |file| + if data = YAML::load(ERB.new(IO.read(file)).result) + data.keys.each do |key| + key_id = Fixtures.identify(key) + + if key == label || key_id == id.to_i + puts "#{file}: #{key} (#{key_id})" + end + end + end + end + end + end + + namespace :schema do + desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" + task :dump => :environment do + require 'active_record/schema_dumper' + File.open(ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb", "w") do |file| + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) + end + Rake::Task["db:schema:dump"].reenable + end + + desc "Load a schema.rb file into the database" + task :load => :environment do + file = ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb" + if File.exists?(file) + load(file) + else + abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{RAILS_ROOT}/config/environment.rb to prevent active_record from loading: config.frameworks -= [ :active_record ]} + end + end + end + + namespace :structure do + desc "Dump the database structure to a SQL file" + task :dump => :environment do + abcs = ActiveRecord::Base.configurations + case abcs[RAILS_ENV]["adapter"] + when "mysql", "oci", "oracle" + ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) + File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + when "postgresql" + ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"] + ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"] + ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"] + search_path = abcs[RAILS_ENV]["schema_search_path"] + search_path = "--schema=#{search_path}" if search_path + `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}` + raise "Error dumping database" if $?.exitstatus == 1 + when "sqlite", "sqlite3" + dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"] + `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql` + when "sqlserver" + `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r` + `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r` + when "firebird" + set_firebird_env(abcs[RAILS_ENV]) + db_string = firebird_db_string(abcs[RAILS_ENV]) + sh "isql -a #{db_string} > #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql" + else + raise "Task not supported by '#{abcs["test"]["adapter"]}'" + end + + if ActiveRecord::Base.connection.supports_migrations? + File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } + end + end + end + + namespace :test do + desc "Recreate the test database from the current schema.rb" + task :load => 'db:test:purge' do + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) + ActiveRecord::Schema.verbose = false + Rake::Task["db:schema:load"].invoke + end + + desc "Recreate the test database from the current environment's database schema" + task :clone => %w(db:schema:dump db:test:load) + + desc "Recreate the test databases from the development structure" + task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do + abcs = ActiveRecord::Base.configurations + case abcs["test"]["adapter"] + when "mysql" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') + IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| + ActiveRecord::Base.connection.execute(table) + end + when "postgresql" + ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] + ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] + ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] + `psql -U "#{abcs["test"]["username"]}" -f #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}` + when "sqlite", "sqlite3" + dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] + `#{abcs["test"]["adapter"]} #{dbfile} < #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql` + when "sqlserver" + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` + when "oci", "oracle" + ActiveRecord::Base.establish_connection(:test) + IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| + ActiveRecord::Base.connection.execute(ddl) + end + when "firebird" + set_firebird_env(abcs["test"]) + db_string = firebird_db_string(abcs["test"]) + sh "isql -i #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{db_string}" + else + raise "Task not supported by '#{abcs["test"]["adapter"]}'" + end + end + + desc "Empty the test database" + task :purge => :environment do + abcs = ActiveRecord::Base.configurations + case abcs["test"]["adapter"] + when "mysql" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"]) + when "postgresql" + ActiveRecord::Base.clear_active_connections! + drop_database(abcs['test']) + create_database(abcs['test']) + when "sqlite","sqlite3" + dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] + File.delete(dbfile) if File.exist?(dbfile) + when "sqlserver" + dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` + when "oci", "oracle" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl| + ActiveRecord::Base.connection.execute(ddl) + end + when "firebird" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.recreate_database! + else + raise "Task not supported by '#{abcs["test"]["adapter"]}'" + end + end + + desc 'Check for pending migrations and load the test schema' + task :prepare => 'db:abort_if_pending_migrations' do + if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank? + Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke + end + end + end + + namespace :sessions do + desc "Creates a sessions migration for use with ActiveRecord::SessionStore" + task :create => :environment do + raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations? + require 'rails_generator' + require 'rails_generator/scripts/generate' + Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "CreateSessions"]) + end + + desc "Clear the sessions table" + task :clear => :environment do + ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}" + end + end +end + +def drop_database(config) + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection.drop_database config['database'] + when /^sqlite/ + FileUtils.rm(File.join(RAILS_ROOT, config['database'])) + when 'postgresql' + ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) + ActiveRecord::Base.connection.drop_database config['database'] + end +end + +def session_table_name + ActiveRecord::Base.pluralize_table_names ? :sessions : :session +end + +def set_firebird_env(config) + ENV["ISC_USER"] = config["username"].to_s if config["username"] + ENV["ISC_PASSWORD"] = config["password"].to_s if config["password"] +end + +def firebird_db_string(config) + FireRuby::Database.db_string_for(config.symbolize_keys) +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/documentation.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/documentation.rake new file mode 100644 index 00000000000..8b41478a93d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/documentation.rake @@ -0,0 +1,88 @@ +namespace :doc do + desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb or title with TITLE=\"Custom Title\"" + Rake::RDocTask.new("app") { |rdoc| + rdoc.rdoc_dir = 'doc/app' + rdoc.template = ENV['template'] if ENV['template'] + rdoc.title = ENV['title'] || "Rails Application Documentation" + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.options << '--charset' << 'utf-8' + rdoc.rdoc_files.include('doc/README_FOR_APP') + rdoc.rdoc_files.include('app/**/*.rb') + rdoc.rdoc_files.include('lib/**/*.rb') + } + + desc "Generate documentation for the Rails framework" + Rake::RDocTask.new("rails") { |rdoc| + rdoc.rdoc_dir = 'doc/api' + rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.title = "Rails Framework Documentation" + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('vendor/rails/railties/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/railties/MIT-LICENSE') + rdoc.rdoc_files.include('vendor/rails/railties/README') + rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,rails_generator/*.rb}') + rdoc.rdoc_files.include('vendor/rails/activerecord/README') + rdoc.rdoc_files.include('vendor/rails/activerecord/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/activerecord/lib/active_record/**/*.rb') + rdoc.rdoc_files.exclude('vendor/rails/activerecord/lib/active_record/vendor/*') + rdoc.rdoc_files.include('vendor/rails/activeresource/README') + rdoc.rdoc_files.include('vendor/rails/activeresource/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/activeresource/lib/active_resource.rb') + rdoc.rdoc_files.include('vendor/rails/activeresource/lib/active_resource/*') + rdoc.rdoc_files.include('vendor/rails/actionpack/README') + rdoc.rdoc_files.include('vendor/rails/actionpack/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_controller/**/*.rb') + rdoc.rdoc_files.include('vendor/rails/actionpack/lib/action_view/**/*.rb') + rdoc.rdoc_files.include('vendor/rails/actionmailer/README') + rdoc.rdoc_files.include('vendor/rails/actionmailer/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/actionmailer/lib/action_mailer/base.rb') + rdoc.rdoc_files.include('vendor/rails/activesupport/README') + rdoc.rdoc_files.include('vendor/rails/activesupport/CHANGELOG') + rdoc.rdoc_files.include('vendor/rails/activesupport/lib/active_support/**/*.rb') + } + + plugins = FileList['vendor/plugins/**'].collect { |plugin| File.basename(plugin) } + + desc "Generate documentation for all installed plugins" + task :plugins => plugins.collect { |plugin| "doc:plugins:#{plugin}" } + + desc "Remove plugin documentation" + task :clobber_plugins do + rm_rf 'doc/plugins' rescue nil + end + + desc "Generate Rails guides" + task :guides do + require File.join(RAILTIES_PATH, "guides/rails_guides") + RailsGuides::Generator.new(File.join(RAILS_ROOT, "doc/guides")).generate + end + + namespace :plugins do + # Define doc tasks for each plugin + plugins.each do |plugin| + desc "Generate documentation for the #{plugin} plugin" + task(plugin => :environment) do + plugin_base = "vendor/plugins/#{plugin}" + options = [] + files = Rake::FileList.new + options << "-o doc/plugins/#{plugin}" + options << "--title '#{plugin.titlecase} Plugin Documentation'" + options << '--line-numbers' << '--inline-source' + options << '--charset' << 'utf-8' + options << '-T html' + + files.include("#{plugin_base}/lib/**/*.rb") + if File.exist?("#{plugin_base}/README") + files.include("#{plugin_base}/README") + options << "--main '#{plugin_base}/README'" + end + files.include("#{plugin_base}/CHANGELOG") if File.exist?("#{plugin_base}/CHANGELOG") + + options << files.to_s + + sh %(rdoc #{options * ' '}) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/framework.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/framework.rake new file mode 100644 index 00000000000..191c9361ff2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/framework.rake @@ -0,0 +1,143 @@ +namespace :rails do + namespace :freeze do + desc "Lock this application to the current gems (by unpacking them into vendor/rails)" + task :gems do + deps = %w(actionpack activerecord actionmailer activesupport activeresource) + require 'rubygems' + require 'rubygems/gem_runner' + + rails = (version = ENV['VERSION']) ? + Gem.cache.find_name('rails', "= #{version}").first : + Gem.cache.find_name('rails').sort_by { |g| g.version }.last + + version ||= rails.version + + unless rails + puts "No rails gem #{version} is installed. Do 'gem list rails' to see what you have available." + exit + end + + puts "Freezing to the gems for Rails #{rails.version}" + rm_rf "vendor/rails" + mkdir_p "vendor/rails" + + begin + chdir("vendor/rails") do + rails.dependencies.select { |g| deps.include? g.name }.each do |g| + Gem::GemRunner.new.run(["unpack", g.name, "--version", g.version_requirements.to_s]) + mv(Dir.glob("#{g.name}*").first, g.name) + end + + Gem::GemRunner.new.run(["unpack", "rails", "--version", "=#{version}"]) + FileUtils.mv(Dir.glob("rails*").first, "railties") + end + rescue Exception + rm_rf "vendor/rails" + raise + end + end + + desc 'Lock to latest Edge Rails, for a specific release use RELEASE=1.2.0' + task :edge do + require 'open-uri' + version = ENV["RELEASE"] || "edge" + target = "rails_#{version}.zip" + commits = "http://github.com/api/v1/yaml/rails/rails/commits/master" + url = "http://dev.rubyonrails.org/archives/#{target}" + + chdir 'vendor' do + latest_revision = YAML.load(open(commits))["commits"].first["id"] + + puts "Downloading Rails from #{url}" + File.open('rails.zip', 'wb') do |dst| + open url do |src| + while chunk = src.read(4096) + dst << chunk + end + end + end + + puts 'Unpacking Rails' + rm_rf 'rails' + `unzip rails.zip` + %w(rails.zip rails/Rakefile rails/cleanlogs.sh rails/pushgems.rb rails/release.rb).each do |goner| + rm_f goner + end + + touch "rails/REVISION_#{latest_revision}" + end + + puts 'Updating current scripts, javascripts, and configuration settings' + Rake::Task['rails:update'].invoke + end + end + + desc "Unlock this application from freeze of gems or edge and return to a fluid use of system gems" + task :unfreeze do + rm_rf "vendor/rails" + end + + desc "Update both configs, scripts and public/javascripts from Rails" + task :update => [ "update:scripts", "update:javascripts", "update:configs", "update:application_controller" ] + + desc "Applies the template supplied by LOCATION=/path/to/template" + task :template do + require 'rails_generator/generators/applications/app/template_runner' + Rails::TemplateRunner.new(ENV["LOCATION"]) + end + + namespace :update do + desc "Add new scripts to the application script/ directory" + task :scripts do + local_base = "script" + edge_base = "#{File.dirname(__FILE__)}/../../bin" + + local = Dir["#{local_base}/**/*"].reject { |path| File.directory?(path) } + edge = Dir["#{edge_base}/**/*"].reject { |path| File.directory?(path) } + + edge.each do |script| + base_name = script[(edge_base.length+1)..-1] + next if base_name == "rails" + next if local.detect { |path| base_name == path[(local_base.length+1)..-1] } + if !File.directory?("#{local_base}/#{File.dirname(base_name)}") + mkdir_p "#{local_base}/#{File.dirname(base_name)}" + end + install script, "#{local_base}/#{base_name}", :mode => 0755 + end + end + + desc "Update your javascripts from your current rails install" + task :javascripts do + require 'railties_path' + project_dir = RAILS_ROOT + '/public/javascripts/' + scripts = Dir[RAILTIES_PATH + '/html/javascripts/*.js'] + scripts.reject!{|s| File.basename(s) == 'application.js'} if File.exist?(project_dir + 'application.js') + FileUtils.cp(scripts, project_dir) + end + + desc "Update config/boot.rb from your current rails install" + task :configs do + require 'railties_path' + FileUtils.cp(RAILTIES_PATH + '/environments/boot.rb', RAILS_ROOT + '/config/boot.rb') + end + + desc "Rename application.rb to application_controller.rb" + task :application_controller do + old_style = RAILS_ROOT + '/app/controllers/application.rb' + new_style = RAILS_ROOT + '/app/controllers/application_controller.rb' + if File.exists?(old_style) && !File.exists?(new_style) + FileUtils.mv(old_style, new_style) + puts "#{old_style} has been renamed to #{new_style}, update your SCM as necessary" + end + end + + desc "Generate dispatcher files in RAILS_ROOT/public" + task :generate_dispatchers do + require 'railties_path' + FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.rb') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.cgi') + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/gems.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/gems.rake new file mode 100644 index 00000000000..f1c34c7ccaf --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/gems.rake @@ -0,0 +1,78 @@ +desc "List the gems that this rails application depends on" +task :gems => 'gems:base' do + Rails.configuration.gems.each do |gem| + print_gem_status(gem) + end + puts + puts "I = Installed" + puts "F = Frozen" + puts "R = Framework (loaded before rails starts)" +end + +namespace :gems do + task :base do + $gems_rake_task = true + require 'rubygems' + require 'rubygems/gem_runner' + Rake::Task[:environment].invoke + end + + desc "Build any native extensions for unpacked gems" + task :build do + $gems_build_rake_task = true + frozen_gems.each { |gem| gem.build } + end + + namespace :build do + desc "Force the build of all gems" + task :force do + $gems_build_rake_task = true + frozen_gems.each { |gem| gem.build(:force => true) } + end + end + + desc "Installs all required gems." + task :install => :base do + current_gems.each { |gem| gem.install } + end + + desc "Unpacks all required gems into vendor/gems." + task :unpack => :install do + current_gems.each { |gem| gem.unpack } + end + + namespace :unpack do + desc "Unpacks all required gems and their dependencies into vendor/gems." + task :dependencies => :install do + current_gems.each { |gem| gem.unpack(:recursive => true) } + end + end + + desc "Regenerate gem specifications in correct format." + task :refresh_specs do + frozen_gems(false).each { |gem| gem.refresh } + end +end + +def current_gems + gems = Rails.configuration.gems + gems = gems.select { |gem| gem.name == ENV['GEM'] } unless ENV['GEM'].blank? + gems +end + +def frozen_gems(load_specs=true) + Dir[File.join(RAILS_ROOT, 'vendor', 'gems', '*-*')].map do |gem_dir| + Rails::GemDependency.from_directory_name(gem_dir, load_specs) + end +end + +def print_gem_status(gem, indent=1) + code = case + when gem.framework_gem? then 'R' + when gem.frozen? then 'F' + when gem.installed? then 'I' + else ' ' + end + puts " "*(indent-1)+" - [#{code}] #{gem.name} #{gem.requirement.to_s}" + gem.dependencies.each { |g| print_gem_status(g, indent+1) } +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/log.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/log.rake new file mode 100644 index 00000000000..6e1334692ee --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/log.rake @@ -0,0 +1,9 @@ +namespace :log do + desc "Truncates all *.log files in log/ to zero bytes" + task :clear do + FileList["log/*.log"].each do |log_file| + f = File.open(log_file, "w") + f.close + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/middleware.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/middleware.rake new file mode 100644 index 00000000000..05f159184e2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/middleware.rake @@ -0,0 +1,7 @@ +desc 'Prints out your Rack middleware stack' +task :middleware => :environment do + ActionController::Dispatcher.middleware.active.each do |middleware| + puts "use #{middleware.inspect}" + end + puts "run ActionController::Dispatcher.new" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/misc.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/misc.rake new file mode 100644 index 00000000000..9e6f96db5b0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/misc.rake @@ -0,0 +1,63 @@ +task :default => :test +task :environment do + $rails_rake_task = true + require(File.join(RAILS_ROOT, 'config', 'environment')) +end + +task :rails_env do + unless defined? RAILS_ENV + RAILS_ENV = ENV['RAILS_ENV'] ||= 'development' + end +end + +desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.' +task :secret do + puts ActiveSupport::SecureRandom.hex(64) +end + +require 'active_support' +namespace :time do + namespace :zones do + desc 'Displays names of all time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6' + task :all do + build_time_zone_list(:all) + end + + desc 'Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset. Results can be filtered with optional OFFSET parameter, e.g., OFFSET=-6' + task :us do + build_time_zone_list(:us_zones) + end + + desc 'Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time' + task :local do + jan_offset = Time.now.beginning_of_year.utc_offset + jul_offset = Time.now.beginning_of_year.change(:month => 7).utc_offset + offset = jan_offset < jul_offset ? jan_offset : jul_offset + build_time_zone_list(:all, offset) + end + + # to find UTC -06:00 zones, OFFSET can be set to either -6, -6:00 or 21600 + def build_time_zone_list(method, offset = ENV['OFFSET']) + if offset + offset = if offset.to_s.match(/(\+|-)?(\d+):(\d+)/) + sign = $1 == '-' ? -1 : 1 + hours, minutes = $2.to_f, $3.to_f + ((hours * 3600) + (minutes.to_f * 60)) * sign + elsif offset.to_f.abs <= 13 + offset.to_f * 3600 + else + offset.to_f + end + end + previous_offset = nil + ActiveSupport::TimeZone.__send__(method).each do |zone| + if offset.nil? || offset == zone.utc_offset + puts "\n* UTC #{zone.formatted_offset} *" unless zone.utc_offset == previous_offset + puts zone.name + previous_offset = zone.utc_offset + end + end + puts "\n" + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/rails.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/rails.rb new file mode 100644 index 00000000000..0600a05a543 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/rails.rb @@ -0,0 +1,9 @@ +$VERBOSE = nil + +# Load Rails rakefile extensions +Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext } + +# Load any custom rakefile extensions +Dir["#{RAILS_ROOT}/vendor/plugins/*/tasks/**/*.rake"].sort.each { |ext| load ext } +Dir["#{RAILS_ROOT}/vendor/plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext } +Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/routes.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/routes.rake new file mode 100644 index 00000000000..abbf3258c18 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/routes.rake @@ -0,0 +1,18 @@ +desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.' +task :routes => :environment do + all_routes = ENV['CONTROLLER'] ? ActionController::Routing::Routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : ActionController::Routing::Routes.routes + routes = all_routes.collect do |route| + name = ActionController::Routing::Routes.named_routes.routes.index(route).to_s + verb = route.conditions[:method].to_s.upcase + segs = route.segments.inject("") { |str,s| str << s.to_s } + segs.chop! if segs.length > 1 + reqs = route.requirements.empty? ? "" : route.requirements.inspect + {:name => name, :verb => verb, :segs => segs, :reqs => reqs} + end + name_width = routes.collect {|r| r[:name]}.collect {|n| n.length}.max + verb_width = routes.collect {|r| r[:verb]}.collect {|v| v.length}.max + segs_width = routes.collect {|r| r[:segs]}.collect {|s| s.length}.max + routes.each do |r| + puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:segs].ljust(segs_width)} #{r[:reqs]}" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/statistics.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/statistics.rake new file mode 100644 index 00000000000..5ab27a0f625 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/statistics.rake @@ -0,0 +1,17 @@ +STATS_DIRECTORIES = [ + %w(Controllers app/controllers), + %w(Helpers app/helpers), + %w(Models app/models), + %w(Libraries lib/), + %w(APIs app/apis), + %w(Integration\ tests test/integration), + %w(Functional\ tests test/functional), + %w(Unit\ tests test/unit) + +].collect { |name, dir| [ name, "#{RAILS_ROOT}/#{dir}" ] }.select { |name, dir| File.directory?(dir) } + +desc "Report code statistics (KLOCs, etc) from the application" +task :stats do + require 'code_statistics' + CodeStatistics.new(*STATS_DIRECTORIES).to_s +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/testing.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/testing.rake new file mode 100644 index 00000000000..fd5e52a05bc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/testing.rake @@ -0,0 +1,139 @@ +TEST_CHANGES_SINCE = Time.now - 600 + +# Look up tests for recently modified sources. +def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago) + FileList[source_pattern].map do |path| + if File.mtime(path) > touched_since + tests = [] + source_dir = File.dirname(path).split("/") + source_file = File.basename(path, '.rb') + + # Support subdirs in app/models and app/controllers + modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path + + # For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb + test = "#{modified_test_path}/#{source_file}_test.rb" + tests.push test if File.exist?(test) + + # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb + test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}" + FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exist?(test) + + return tests + + end + end.flatten.compact +end + + +# Recreated here from ActiveSupport because :uncommitted needs it before Rails is available +module Kernel + def silence_stderr + old_stderr = STDERR.dup + STDERR.reopen(RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'NUL:' : '/dev/null') + STDERR.sync = true + yield + ensure + STDERR.reopen(old_stderr) + end +end + +desc 'Run all unit, functional and integration tests' +task :test do + errors = %w(test:units test:functionals test:integration).collect do |task| + begin + Rake::Task[task].invoke + nil + rescue => e + task + end + end.compact + abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any? +end + +namespace :test do + Rake::TestTask.new(:recent => "db:test:prepare") do |t| + since = TEST_CHANGES_SINCE + touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } + + recent_tests('app/models/**/*.rb', 'test/unit', since) + + recent_tests('app/controllers/**/*.rb', 'test/functional', since) + + t.libs << 'test' + t.verbose = true + t.test_files = touched.uniq + end + Rake::Task['test:recent'].comment = "Test recent changes" + + Rake::TestTask.new(:uncommitted => "db:test:prepare") do |t| + def t.file_list + if File.directory?(".svn") + changed_since_checkin = silence_stderr { `svn status` }.map { |path| path.chomp[7 .. -1] } + elsif File.directory?(".git") + changed_since_checkin = silence_stderr { `git ls-files --modified --others` }.map { |path| path.chomp } + else + abort "Not a Subversion or Git checkout." + end + + models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ } + controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ } + + unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" } + functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" } + + unit_tests.uniq + functional_tests.uniq + end + + t.libs << 'test' + t.verbose = true + end + Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)" + + Rake::TestTask.new(:units => "db:test:prepare") do |t| + t.libs << "test" + t.pattern = 'test/unit/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:units'].comment = "Run the unit tests in test/unit" + + Rake::TestTask.new(:functionals => "db:test:prepare") do |t| + t.libs << "test" + t.pattern = 'test/functional/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional" + + Rake::TestTask.new(:integration => "db:test:prepare") do |t| + t.libs << "test" + t.pattern = 'test/integration/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:integration'].comment = "Run the integration tests in test/integration" + + Rake::TestTask.new(:benchmark => 'db:test:prepare') do |t| + t.libs << 'test' + t.pattern = 'test/performance/**/*_test.rb' + t.verbose = true + t.options = '-- --benchmark' + end + Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests' + + Rake::TestTask.new(:profile => 'db:test:prepare') do |t| + t.libs << 'test' + t.pattern = 'test/performance/**/*_test.rb' + t.verbose = true + end + Rake::Task['test:profile'].comment = 'Profile the performance tests' + + Rake::TestTask.new(:plugins => :environment) do |t| + t.libs << "test" + + if ENV['PLUGIN'] + t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb" + else + t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb' + end + + t.verbose = true + end + Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/tmp.rake b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/tmp.rake new file mode 100644 index 00000000000..fea15058bb8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/tasks/tmp.rake @@ -0,0 +1,37 @@ +namespace :tmp do + desc "Clear session, cache, and socket files from tmp/" + task :clear => [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"] + + desc "Creates tmp directories for sessions, cache, sockets, and pids" + task :create do + FileUtils.mkdir_p(%w( tmp/sessions tmp/cache tmp/sockets tmp/pids )) + end + + namespace :sessions do + desc "Clears all files in tmp/sessions" + task :clear do + FileUtils.rm(Dir['tmp/sessions/[^.]*']) + end + end + + namespace :cache do + desc "Clears all files and directories in tmp/cache" + task :clear do + FileUtils.rm_rf(Dir['tmp/cache/[^.]*']) + end + end + + namespace :sockets do + desc "Clears all files in tmp/sockets" + task :clear do + FileUtils.rm(Dir['tmp/sockets/[^.]*']) + end + end + + namespace :pids do + desc "Clears all files in tmp/pids" + task :clear do + FileUtils.rm(Dir['tmp/pids/[^.]*']) + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/test_help.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/test_help.rb new file mode 100644 index 00000000000..f8608925d55 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/test_help.rb @@ -0,0 +1,38 @@ +# Make double-sure the RAILS_ENV is set to test, +# so fixtures are loaded to the right database +silence_warnings { RAILS_ENV = "test" } + +require 'test/unit' +require 'action_controller/test_case' +require 'action_view/test_case' +require 'action_controller/integration' +require 'action_mailer/test_case' if defined?(ActionMailer) + +if defined?(ActiveRecord) + require 'active_record/test_case' + require 'active_record/fixtures' + + class ActiveSupport::TestCase + include ActiveRecord::TestFixtures + self.fixture_path = "#{RAILS_ROOT}/test/fixtures/" + self.use_instantiated_fixtures = false + self.use_transactional_fixtures = true + end + + ActionController::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path + + def create_fixtures(*table_names, &block) + Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block) + end +end + +begin + require_library_or_gem 'ruby-debug' + Debugger.start + if Debugger.respond_to?(:settings) + Debugger.settings[:autoeval] = true + Debugger.settings[:autolist] = 1 + end +rescue LoadError + # ruby-debug wasn't available so neither can the debugging be +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/webrick_server.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/webrick_server.rb new file mode 100644 index 00000000000..2f60151b222 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/lib/webrick_server.rb @@ -0,0 +1,156 @@ +# Donated by Florian Gross + +require 'webrick' +require 'cgi' +require 'stringio' +require 'dispatcher' + +include WEBrick + +class CGI #:nodoc: + def stdinput + @stdin || $stdin + end + + def env_table + @env_table || ENV + end + + def initialize(type = "query", table = nil, stdin = nil) + @env_table, @stdin = table, stdin + + if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE") + Apache.request.setup_cgi_env + end + + extend QueryExtension + @multipart = false + if defined?(CGI_PARAMS) + warn "do not use CGI_PARAMS and CGI_COOKIES" + @params = CGI_PARAMS.dup + @cookies = CGI_COOKIES.dup + else + initialize_query() # set @params, @cookies + end + @output_cookies = nil + @output_hidden = nil + end +end + +# A custom dispatch servlet for use with WEBrick. It dispatches requests +# (using the Rails Dispatcher) to the appropriate controller/action. By default, +# it restricts WEBrick to a managing a single Rails request at a time, but you +# can change this behavior by setting ActionController::Base.allow_concurrency +# to true. +class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet + # Start the WEBrick server with the given options, mounting the + # DispatchServlet at /. + def self.dispatch(options = {}) + Socket.do_not_reverse_lookup = true # patch for OS X + + params = { :Port => options[:port].to_i, + :ServerType => options[:server_type], + :BindAddress => options[:ip] } + params[:MimeTypes] = options[:mime_types] if options[:mime_types] + + server = WEBrick::HTTPServer.new(params) + server.mount('/', DispatchServlet, options) + + trap("INT") { server.shutdown } + server.start + end + + def initialize(server, options) #:nodoc: + @server_options = options + @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root]) + # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/") + # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb + Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory']) + super + end + + def service(req, res) #:nodoc: + unless handle_file(req, res) + unless handle_dispatch(req, res) + raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." + end + end + end + + def handle_file(req, res) #:nodoc: + begin + req = req.dup + path = req.path.dup + + # Add .html if the last path piece has no . in it + path << '.html' if path != '/' && (%r{(^|/)[^./]+$} =~ path) + path.gsub!('+', ' ') # Unescape + since FileHandler doesn't do so. + + req.instance_variable_set(:@path_info, path) # Set the modified path... + + @file_handler.send(:service, req, res) + return true + rescue HTTPStatus::PartialContent, HTTPStatus::NotModified => err + res.set_error(err) + return true + rescue => err + return false + end + end + + def handle_dispatch(req, res, origin = nil) #:nodoc: + data = StringIO.new + Dispatcher.dispatch( + CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")), + ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, + data + ) + + header, body = extract_header_and_body(data) + + set_charset(header) + assign_status(res, header) + res.cookies.concat(header.delete('set-cookie') || []) + header.each { |key, val| res[key] = val.join(", ") } + + res.body = body + return true + rescue => err + p err, err.backtrace + return false + end + + private + def create_env_table(req, origin) + env = req.meta_vars.clone + env.delete "SCRIPT_NAME" + env["QUERY_STRING"] = req.request_uri.query + env["REQUEST_URI"] = origin if origin + return env + end + + def extract_header_and_body(data) + data.rewind + data = data.read + + raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2) + header = WEBrick::HTTPUtils::parse_header(raw_header) + + return header, body + end + + def set_charset(header) + ct = header["content-type"] + if ct.any? { |x| x =~ /^text\// } && ! ct.any? { |x| x =~ /charset=/ } + ch = @server_options[:charset] || "UTF-8" + ct.find { |x| x =~ /^text\// } << ("; charset=" + ch) + end + end + + def assign_status(res, header) + if /^(\d+)/ =~ header['status'][0] + res.status = $1.to_i + header.delete('status') + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/abstract_unit.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/abstract_unit.rb new file mode 100644 index 00000000000..85f680da822 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/abstract_unit.rb @@ -0,0 +1,27 @@ +$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" +$:.unshift File.dirname(__FILE__) + "/../../activerecord/lib" +$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" +$:.unshift File.dirname(__FILE__) + "/../../actionmailer/lib" +$:.unshift File.dirname(__FILE__) + "/../lib" +$:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" + +require 'stringio' +require 'rubygems' +require 'test/unit' + +require 'active_support' +require 'active_support/test_case' + +if defined?(RAILS_ROOT) + RAILS_ROOT.replace File.dirname(__FILE__) +else + RAILS_ROOT = File.dirname(__FILE__) +end + +def uses_gem(gem_name, test_name, version = '> 0') + gem gem_name.to_s, version + require gem_name.to_s + yield +rescue LoadError + $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/backtrace_cleaner_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/backtrace_cleaner_test.rb new file mode 100644 index 00000000000..6319aeeab05 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/backtrace_cleaner_test.rb @@ -0,0 +1,61 @@ +require 'abstract_unit' + +require 'initializer' +require 'rails/backtrace_cleaner' + +if defined? Test::Unit::Util::BacktraceFilter + class TestWithBacktrace + include Test::Unit::Util::BacktraceFilter + include Rails::BacktraceFilterForTestUnit + end + + class BacktraceCleanerFilterTest < ActiveSupport::TestCase + def setup + @test = TestWithBacktrace.new + @backtrace = [ './test/rails/benchmark_test.rb', './test/rails/dependencies.rb', '/opt/local/lib/ruby/kernel.rb' ] + end + + test "test with backtrace should use the rails backtrace cleaner to clean" do + Rails.stubs(:backtrace_cleaner).returns(stub(:clean)) + Rails.backtrace_cleaner.expects(:clean).with(@backtrace, nil) + @test.send(:filter_backtrace, @backtrace) + end + + test "filter backtrace should have the same arity as Test::Unit::Util::BacktraceFilter" do + assert_nothing_raised do + @test.send(:filter_backtrace, @backtrace, '/opt/local/lib') + end + end + end +else + $stderr.puts 'No BacktraceFilter for minitest' +end + +class BacktraceCleanerVendorGemTest < ActiveSupport::TestCase + def setup + @cleaner = Rails::BacktraceCleaner.new + end + + test "should format installed gems correctly" do + @backtrace = [ "#{Gem.path[0]}/gems/nosuchgem-1.2.3/lib/foo.rb" ] + @result = @cleaner.clean(@backtrace) + assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0] + end + + test "should format installed gems not in Gem.default_dir correctly" do + @target_dir = Gem.path.detect { |p| p != Gem.default_dir } + # skip this test if default_dir is the only directory on Gem.path + if @target_dir + @backtrace = [ "#{@target_dir}/gems/nosuchgem-1.2.3/lib/foo.rb" ] + @result = @cleaner.clean(@backtrace) + assert_equal "nosuchgem (1.2.3) lib/foo.rb", @result[0] + end + end + + test "should format vendor gems correctly" do + @backtrace = [ "#{Rails::GemDependency.unpacked_path}/nosuchgem-1.2.3/lib/foo.rb" ] + @result = @cleaner.clean(@backtrace) + assert_equal "nosuchgem (1.2.3) [v] lib/foo.rb", @result[0] + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/boot_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/boot_test.rb new file mode 100644 index 00000000000..08fcc82e6f4 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/boot_test.rb @@ -0,0 +1,178 @@ +require 'abstract_unit' +require 'initializer' +require "#{File.dirname(__FILE__)}/../environments/boot" + +class BootTest < Test::Unit::TestCase + def test_boot_returns_if_booted + Rails.expects(:booted?).returns(true) + Rails.expects(:pick_boot).never + assert_nil Rails.boot! + end + + def test_boot_preinitializes_then_picks_and_runs_if_not_booted + Rails.expects(:booted?).returns(false) + Rails.expects(:preinitialize) + Rails.expects(:pick_boot).returns(mock(:run => 'result')) + assert_equal 'result', Rails.boot! + end + + def test_preinitialize_does_not_raise_exception_if_preinitializer_file_does_not_exist + Rails.stubs(:preinitializer_path).returns('/there/is/no/such/file') + + assert_nothing_raised { Rails.preinitialize } + end + + def test_load_preinitializer_loads_preinitializer_file + Rails.stubs(:preinitializer_path).returns("#{File.dirname(__FILE__)}/fixtures/environment_with_constant.rb") + + assert_nil $initialize_test_set_from_env + Rails.preinitialize + assert_equal "success", $initialize_test_set_from_env + ensure + $initialize_test_set_from_env = nil + end + + def test_boot_vendor_rails_by_default + Rails.expects(:booted?).returns(false) + Rails.expects(:preinitialize) + File.expects(:exist?).with("#{RAILS_ROOT}/vendor/rails").returns(true) + Rails::VendorBoot.any_instance.expects(:run).returns('result') + assert_equal 'result', Rails.boot! + end + + def test_boot_gem_rails_otherwise + Rails.expects(:booted?).returns(false) + Rails.expects(:preinitialize) + File.expects(:exist?).with("#{RAILS_ROOT}/vendor/rails").returns(false) + Rails::GemBoot.any_instance.expects(:run).returns('result') + assert_equal 'result', Rails.boot! + end + + def test_run_loads_initializer_and_sets_load_path + boot = Rails::Boot.new + boot.expects(:load_initializer) + Rails::Initializer.expects(:run).with(:set_load_path) + boot.run + end +end + +class VendorBootTest < Test::Unit::TestCase + include Rails + + def test_load_initializer_requires_from_vendor_rails + boot = VendorBoot.new + boot.expects(:require).with("#{RAILS_ROOT}/vendor/rails/railties/lib/initializer") + Rails::Initializer.expects(:run).with(:install_gem_spec_stubs) + Rails::GemDependency.expects(:add_frozen_gem_path) + boot.load_initializer + end +end + +class GemBootTest < Test::Unit::TestCase + include Rails + + def test_load_initializer_loads_rubygems_and_the_rails_gem + boot = GemBoot.new + GemBoot.expects(:load_rubygems) + boot.expects(:load_rails_gem) + boot.expects(:require).with('initializer') + boot.load_initializer + end + + def test_load_rubygems_exits_with_error_if_missing + GemBoot.expects(:require).with('rubygems').raises(LoadError, 'missing rubygems') + STDERR.expects(:puts) + GemBoot.expects(:exit).with(1) + GemBoot.load_rubygems + end + + def test_load_rubygems_exits_with_error_if_too_old + GemBoot.stubs(:rubygems_version).returns('0.0.1') + GemBoot.expects(:require).with('rubygems').returns(true) + STDERR.expects(:puts) + GemBoot.expects(:exit).with(1) + GemBoot.load_rubygems + end + + def test_load_rails_gem_activates_specific_gem_if_version_given + GemBoot.stubs(:gem_version).returns('0.0.1') + + boot = GemBoot.new + boot.expects(:gem).with('rails', '0.0.1') + boot.load_rails_gem + end + + def test_load_rails_gem_activates_latest_gem_if_no_version_given + GemBoot.stubs(:gem_version).returns(nil) + + boot = GemBoot.new + boot.expects(:gem).with('rails') + boot.load_rails_gem + end + + def test_load_rails_gem_exits_with_error_if_missing + GemBoot.stubs(:gem_version).returns('0.0.1') + + boot = GemBoot.new + boot.expects(:gem).with('rails', '0.0.1').raises(Gem::LoadError, 'missing rails 0.0.1 gem') + STDERR.expects(:puts) + boot.expects(:exit).with(1) + boot.load_rails_gem + end +end + +class ParseGemVersionTest < Test::Unit::TestCase + def test_should_return_nil_if_no_lines_are_passed + assert_equal nil, parse('') + assert_equal nil, parse(nil) + end + + def test_should_accept_either_single_or_double_quotes + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse('RAILS_GEM_VERSION = "1.2.3"') + end + + def test_should_return_nil_if_no_lines_match + assert_equal nil, parse('nothing matches on this line\nor on this line') + end + + def test_should_parse_with_no_leading_space + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION") + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3'") + end + + def test_should_parse_with_any_number_of_leading_spaces + assert_equal nil, parse([]) + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION") + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3' unless defined? RAILS_GEM_VERSION") + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse(" RAILS_GEM_VERSION = '1.2.3'") + end + + def test_should_ignore_unrelated_comments + assert_equal "1.2.3", parse("# comment\nRAILS_GEM_VERSION = '1.2.3'\n# comment") + end + + def test_should_ignore_commented_version_lines + assert_equal "1.2.3", parse("#RAILS_GEM_VERSION = '9.8.7'\nRAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse("# RAILS_GEM_VERSION = '9.8.7'\nRAILS_GEM_VERSION = '1.2.3'") + assert_equal "1.2.3", parse("RAILS_GEM_VERSION = '1.2.3'\n# RAILS_GEM_VERSION = '9.8.7'") + end + + def test_should_allow_advanced_rubygems_version_specifications + # See http://rubygems.org/read/chapter/16 + assert_equal "=1.2.3", parse("RAILS_GEM_VERSION = '=1.2.3'") # equal sign + assert_equal "= 1.2.3", parse("RAILS_GEM_VERSION = '= 1.2.3'") # with space + assert_equal "!=1.2.3", parse("RAILS_GEM_VERSION = '!=1.2.3'") # not equal + assert_equal ">1.2.3", parse("RAILS_GEM_VERSION = '>1.2.3'") # greater than + assert_equal "<1.2.3", parse("RAILS_GEM_VERSION = '<1.2.3'") # less than + assert_equal ">=1.2.3", parse("RAILS_GEM_VERSION = '>=1.2.3'") # greater than or equal + assert_equal "<=1.2.3", parse("RAILS_GEM_VERSION = '<=1.2.3'") # less than or equal + assert_equal "~>1.2.3.0", parse("RAILS_GEM_VERSION = '~>1.2.3.0'") # approximately greater than + end + + private + def parse(text) + Rails::GemBoot.parse_gem_version(text) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/console_app_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/console_app_test.rb new file mode 100644 index 00000000000..f11de087e35 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/console_app_test.rb @@ -0,0 +1,42 @@ +require 'abstract_unit' + +require 'action_controller' # console_app uses 'action_controller/integration' + +unless defined? ApplicationController + class ApplicationController < ActionController::Base; end + ActionController::Base.session_store = nil +end + +require 'dispatcher' +require 'console_app' + +# console_app sets Test::Unit.run to work around the at_exit hook in test/unit, which kills IRB +if Test::Unit.respond_to?(:run=) + Test::Unit.run = false + + class ConsoleAppTest < Test::Unit::TestCase + def test_app_method_should_return_integration_session + assert_nothing_thrown do + console_session = app + assert_not_nil console_session + assert_instance_of ActionController::Integration::Session, + console_session + end + end + + def test_reload_should_fire_preparation_callbacks + a = b = c = nil + + Dispatcher.to_prepare { a = b = c = 1 } + Dispatcher.to_prepare { b = c = 2 } + Dispatcher.to_prepare { c = 3 } + ActionController::Routing::Routes.expects(:reload) + + reload! + + assert_equal 1, a + assert_equal 2, b + assert_equal 3, c + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fcgi_dispatcher_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fcgi_dispatcher_test.rb new file mode 100644 index 00000000000..ebbf459ab27 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fcgi_dispatcher_test.rb @@ -0,0 +1,266 @@ +require 'abstract_unit' + +if RUBY_VERSION < '1.9.0' +uses_gem "fcgi", "0.8.7" do + +require 'action_controller' +require 'fcgi_handler' + +Dispatcher.middleware.clear + +class RailsFCGIHandlerTest < Test::Unit::TestCase + def setup + @log = StringIO.new + @handler = RailsFCGIHandler.new(@log) + end + + def test_process_restart + request = mock + FCGI.stubs(:each).yields(request) + + @handler.expects(:process_request).once + @handler.expects(:dispatcher_error).never + + @handler.expects(:when_ready).returns(:restart) + @handler.expects(:close_connection).with(request) + @handler.expects(:reload!).never + @handler.expects(:restart!) + + @handler.process! + end + + def test_process_exit + request = mock + FCGI.stubs(:each).yields(request) + + @handler.expects(:process_request).once + @handler.expects(:dispatcher_error).never + + @handler.expects(:when_ready).returns(:exit) + @handler.expects(:close_connection).with(request) + @handler.expects(:reload!).never + @handler.expects(:restart!).never + + @handler.process! + end + + def test_process_with_system_exit_exception + request = mock + FCGI.stubs(:each).yields(request) + + @handler.expects(:process_request).once.raises(SystemExit) + @handler.stubs(:dispatcher_log) + @handler.expects(:dispatcher_log).with(:info, regexp_matches(/^stopping/)) + @handler.expects(:dispatcher_error).never + + @handler.expects(:when_ready).never + @handler.expects(:close_connection).never + @handler.expects(:reload!).never + @handler.expects(:restart!).never + + @handler.process! + end + + def test_restart_handler_outside_request + @handler.expects(:dispatcher_log).with(:info, "asked to restart ASAP") + @handler.expects(:restart!).once + + @handler.send(:restart_handler, nil) + assert_equal nil, @handler.when_ready + end + + def test_install_signal_handler_should_log_on_bad_signal + @handler.stubs(:trap).raises(ArgumentError) + + @handler.expects(:dispatcher_log).with(:warn, "Ignoring unsupported signal CHEESECAKE.") + @handler.send(:install_signal_handler, "CHEESECAKE", nil) + end + + def test_reload + @handler.expects(:restore!) + @handler.expects(:dispatcher_log).with(:info, "reloaded") + + @handler.send(:reload!) + assert_nil @handler.when_ready + end + + + def test_reload_runs_gc_when_gc_request_period_set + @handler.expects(:run_gc!) + @handler.expects(:restore!) + @handler.expects(:dispatcher_log).with(:info, "reloaded") + @handler.gc_request_period = 10 + @handler.send(:reload!) + end + + def test_reload_doesnt_run_gc_if_gc_request_period_isnt_set + @handler.expects(:run_gc!).never + @handler.expects(:restore!) + @handler.expects(:dispatcher_log).with(:info, "reloaded") + @handler.send(:reload!) + end + + def test_restart! + @handler.expects(:dispatcher_log).with(:info, "restarted") + @handler.expects(:exec).returns('restarted') + assert_equal 'restarted', @handler.send(:restart!) + end + + def test_restore! + $".expects(:replace) + Dispatcher.expects(:reset_application!) + ActionController::Routing::Routes.expects(:reload) + @handler.send(:restore!) + end + + def test_uninterrupted_processing + request = mock + FCGI.expects(:each).yields(request) + @handler.expects(:process_request).with(request) + + @handler.process! + + assert_nil @handler.when_ready + end +end + + +class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase + class ::RailsFCGIHandler + attr_accessor :signal + alias_method :old_gc_countdown, :gc_countdown + def gc_countdown + signal ? Process.kill(signal, $$) : old_gc_countdown + end + end + + def setup + @log = StringIO.new + @handler = RailsFCGIHandler.new(@log) + @dispatcher = mock + Dispatcher.stubs(:new).returns(@dispatcher) + end + + def test_interrupted_via_HUP_when_not_in_request + request = mock + FCGI.expects(:each).once.yields(request) + @handler.expects(:signal).times(2).returns('HUP') + + @handler.expects(:reload!).once + @handler.expects(:close_connection).never + @handler.expects(:exit).never + + @handler.process! + assert_equal :reload, @handler.when_ready + end + + def test_interrupted_via_USR1_when_not_in_request + request = mock + FCGI.expects(:each).once.yields(request) + @handler.expects(:signal).times(2).returns('USR1') + @handler.expects(:exit_handler).never + + @handler.expects(:reload!).never + @handler.expects(:close_connection).with(request).once + @handler.expects(:exit).never + + @handler.process! + assert_nil @handler.when_ready + end + + def test_restart_via_USR2_when_in_request + request = mock + FCGI.expects(:each).once.yields(request) + @handler.expects(:signal).times(2).returns('USR2') + @handler.expects(:exit_handler).never + + @handler.expects(:reload!).never + @handler.expects(:close_connection).with(request).once + @handler.expects(:exit).never + @handler.expects(:restart!).once + + @handler.process! + assert_equal :restart, @handler.when_ready + end + + def test_interrupted_via_TERM + request = mock + FCGI.expects(:each).once.yields(request) + ::Rack::Handler::FastCGI.expects(:serve).once.returns('TERM') + + @handler.expects(:reload!).never + @handler.expects(:close_connection).never + + @handler.process! + assert_nil @handler.when_ready + end + + def test_runtime_exception_in_fcgi + error = RuntimeError.new('foo') + FCGI.expects(:each).times(2).raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^retrying/)) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) + @handler.process! + end + + def test_runtime_error_in_dispatcher + request = mock + error = RuntimeError.new('foo') + FCGI.expects(:each).once.yields(request) + ::Rack::Handler::FastCGI.expects(:serve).once.raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/)) + @handler.process! + end + + def test_signal_exception_in_fcgi + error = SignalException.new('USR2') + FCGI.expects(:each).once.raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) + @handler.process! + end + + def test_signal_exception_in_dispatcher + request = mock + error = SignalException.new('USR2') + FCGI.expects(:each).once.yields(request) + ::Rack::Handler::FastCGI.expects(:serve).once.raises(error) + @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) + @handler.process! + end +end + + +class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase + def setup + @log = StringIO.new + end + + def teardown + GC.enable + end + + def test_normal_gc + @handler = RailsFCGIHandler.new(@log) + assert_nil @handler.gc_request_period + + # When GC is enabled, GC.disable disables and returns false. + assert_equal false, GC.disable + end + + def test_periodic_gc + @handler = RailsFCGIHandler.new(@log, 10) + assert_equal 10, @handler.gc_request_period + + request = mock + FCGI.expects(:each).times(10).yields(request) + + @handler.expects(:run_gc!).never + 9.times { @handler.process! } + @handler.expects(:run_gc!).once + @handler.process! + + assert_nil @handler.when_ready + end +end +end # uses_gem "fcgi" +end # exclude 1.9 diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml new file mode 100644 index 00000000000..fe80872a168 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml @@ -0,0 +1 @@ +# an empty YAML file - any content in here seems to get parsed as a string \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb new file mode 100644 index 00000000000..d4262f89716 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb @@ -0,0 +1 @@ +# intentionally empty \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb new file mode 100644 index 00000000000..d4262f89716 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb @@ -0,0 +1 @@ +# intentionally empty \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/eager/zoo.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/eager/zoo.rb new file mode 100644 index 00000000000..8b10ef984ba --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/eager/zoo.rb @@ -0,0 +1,3 @@ +class Zoo + include ReptileHouse +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/eager/zoo/reptile_house.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/eager/zoo/reptile_house.rb new file mode 100644 index 00000000000..82bbafce797 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/eager/zoo/reptile_house.rb @@ -0,0 +1,2 @@ +module Zoo::ReptileHouse +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/environment_with_constant.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/environment_with_constant.rb new file mode 100644 index 00000000000..23e1f7afd91 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/environment_with_constant.rb @@ -0,0 +1 @@ +$initialize_test_set_from_env = 'success' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/lib/generators/working/working_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/lib/generators/working/working_generator.rb new file mode 100644 index 00000000000..465b34319ae --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/lib/generators/working/working_generator.rb @@ -0,0 +1,2 @@ +class WorkingGenerator < Rails::Generator::NamedBase +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb new file mode 100644 index 00000000000..2d373ce4220 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/multiplemetals/app/metal/metal_a.rb @@ -0,0 +1,5 @@ +class MetalA < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb new file mode 100644 index 00000000000..a8bbf3fd607 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/multiplemetals/app/metal/metal_b.rb @@ -0,0 +1,5 @@ +class MetalB < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/pluralmetal/app/metal/legacy_routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/pluralmetal/app/metal/legacy_routes.rb new file mode 100644 index 00000000000..0cd3737c321 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/pluralmetal/app/metal/legacy_routes.rb @@ -0,0 +1,5 @@ +class LegacyRoutes < Rails::Rack::Metal + def self.call(env) + [301, { "Location" => "http://example.com"}, []] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/singlemetal/app/metal/foo_metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/singlemetal/app/metal/foo_metal.rb new file mode 100644 index 00000000000..5f5b0875928 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/singlemetal/app/metal/foo_metal.rb @@ -0,0 +1,5 @@ +class FooMetal < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_a.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_a.rb new file mode 100644 index 00000000000..25b3bb0abc6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_a.rb @@ -0,0 +1,7 @@ +module Folder + class MetalA < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_b.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_b.rb new file mode 100644 index 00000000000..7583363f714 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/metal/subfolders/app/metal/Folder/metal_b.rb @@ -0,0 +1,7 @@ +module Folder + class MetalB < Rails::Rack::Metal + def self.call(env) + [200, { "Content-Type" => "text/html"}, ["Hi"]] + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/alternate/a/generators/a_generator/a_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/alternate/a/generators/a_generator/a_generator.rb new file mode 100644 index 00000000000..b33f2dad181 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/alternate/a/generators/a_generator/a_generator.rb @@ -0,0 +1,4 @@ +class AGenerator < Rails::Generator::Base + def manifest + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/init.rb new file mode 100644 index 00000000000..6a771b5b685 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/init.rb @@ -0,0 +1 @@ +raise 'This init.rb should not be evaluated because rails/init.rb exists' diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/lib/gemlike.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/lib/gemlike.rb new file mode 100644 index 00000000000..2088103e459 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/lib/gemlike.rb @@ -0,0 +1,2 @@ +module Gemlike +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/rails/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/rails/init.rb new file mode 100644 index 00000000000..171a293eb3b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/gemlike/rails/init.rb @@ -0,0 +1,7 @@ +# I have access to my directory and the Rails config. +raise 'directory expected but undefined in init.rb' unless defined? directory +raise 'config expected but undefined in init.rb' unless defined? config + +# My lib/ dir must be in the load path. +require 'gemlike' +raise 'missing mixin from my lib/ dir' unless defined? Gemlike diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/plugin_with_no_lib_dir/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/plugin_with_no_lib_dir/init.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/about.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/about.yml new file mode 100644 index 00000000000..d85a7cc0e3f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/about.yml @@ -0,0 +1,2 @@ +author: Plugin Author +version: 1.0.0 \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb new file mode 100644 index 00000000000..8fda8197d19 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb @@ -0,0 +1,4 @@ +class StubbyGenerator < Rails::Generator::Base + def manifest + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/init.rb new file mode 100644 index 00000000000..81beeb0d322 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/init.rb @@ -0,0 +1,7 @@ +# I have access to my directory and the Rails config. +raise 'directory expected but undefined in init.rb' unless defined? directory +raise 'config expected but undefined in init.rb' unless defined? config + +# My lib/ dir must be in the load path. +require 'stubby_mixin' +raise 'missing mixin from my lib/ dir' unless defined? StubbyMixin diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb new file mode 100644 index 00000000000..2d569e5002a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb @@ -0,0 +1,2 @@ +module StubbyMixin +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/controllers/engine_controller.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/controllers/engine_controller.rb new file mode 100644 index 00000000000..323ee1c4dcc --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/controllers/engine_controller.rb @@ -0,0 +1,2 @@ +class EngineController +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb new file mode 100644 index 00000000000..d67a127ca78 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/metal/engine_metal.rb @@ -0,0 +1,10 @@ +class EngineMetal + def self.call(env) + if env["PATH_INFO"] =~ /^\/metal/ + [200, {"Content-Type" => "text/html"}, ["Engine metal"]] + else + [404, {"Content-Type" => "text/html"}, ["Not Found"]] + end + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/models/engine_model.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/models/engine_model.rb new file mode 100644 index 00000000000..e265712185e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/app/models/engine_model.rb @@ -0,0 +1,2 @@ +class EngineModel +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml new file mode 100644 index 00000000000..641a7e035c1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml @@ -0,0 +1,2 @@ +en: + hello: "Hello from Engine" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/config/routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/config/routes.rb new file mode 100644 index 00000000000..cca8d1b1466 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/config/routes.rb @@ -0,0 +1,3 @@ +ActionController::Routing::Routes.draw do |map| + map.connect '/engine', :controller => "engine" +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/init.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/init.rb new file mode 100644 index 00000000000..64e9ae6c309 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/plugins/engines/engine/init.rb @@ -0,0 +1,3 @@ +# My app/models dir must be in the load path. +require 'engine_model' +raise LoadError, 'missing model from my app/models dir' unless defined?(EngineModel) diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/foo/bar.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/foo/bar.html new file mode 100644 index 00000000000..9a35646205e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/foo/bar.html @@ -0,0 +1 @@ +/foo/bar.html \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/foo/index.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/foo/index.html new file mode 100644 index 00000000000..497a2e898f0 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/foo/index.html @@ -0,0 +1 @@ +/foo/index.html \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/index.html b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/index.html new file mode 100644 index 00000000000..525950ba6b8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/fixtures/public/index.html @@ -0,0 +1 @@ +/index.html \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/gem_dependency_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/gem_dependency_test.rb new file mode 100644 index 00000000000..a4cf6f7661b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/gem_dependency_test.rb @@ -0,0 +1,219 @@ +require 'plugin_test_helper' + +class Rails::GemDependency + public :install_command, :unpack_command +end + +Rails::VendorGemSourceIndex.silence_spec_warnings = true + +class GemDependencyTest < Test::Unit::TestCase + def setup + @gem = Rails::GemDependency.new "xhpricotx" + @gem_with_source = Rails::GemDependency.new "xhpricotx", :source => "http://code.whytheluckystiff.net" + @gem_with_version = Rails::GemDependency.new "xhpricotx", :version => "= 0.6" + @gem_with_lib = Rails::GemDependency.new "xaws-s3x", :lib => "aws/s3" + @gem_without_load = Rails::GemDependency.new "xhpricotx", :lib => false + end + + def test_configuration_adds_gem_dependency + config = Rails::Configuration.new + config.gem "xaws-s3x", :lib => "aws/s3", :version => "0.4.0" + assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect { |g| g.install_command } + end + + def test_gem_creates_install_command + assert_equal %w(install xhpricotx), @gem.install_command + end + + def test_gem_with_source_creates_install_command + assert_equal %w(install xhpricotx --source http://code.whytheluckystiff.net), @gem_with_source.install_command + end + + def test_gem_with_version_creates_install_command + assert_equal ["install", "xhpricotx", "--version", '"= 0.6"'], @gem_with_version.install_command + end + + def test_gem_creates_unpack_command + assert_equal %w(unpack xhpricotx), @gem.unpack_command + end + + def test_gem_with_version_unpack_install_command + # stub out specification method, or else test will fail if hpricot 0.6 isn't installed + mock_spec = mock() + mock_spec.stubs(:version).returns('0.6') + @gem_with_version.stubs(:specification).returns(mock_spec) + assert_equal ["unpack", "xhpricotx", "--version", '= 0.6'], @gem_with_version.unpack_command + end + + def test_gem_adds_load_paths + @gem.expects(:gem).with(@gem) + @gem.add_load_paths + end + + def test_gem_with_version_adds_load_paths + @gem_with_version.expects(:gem).with(@gem_with_version) + @gem_with_version.add_load_paths + assert @gem_with_version.load_paths_added? + end + + def test_gem_loading + @gem.expects(:gem).with(@gem) + @gem.expects(:require).with(@gem.name) + @gem.add_load_paths + @gem.load + assert @gem.loaded? + end + + def test_gem_with_lib_loading + @gem_with_lib.expects(:gem).with(@gem_with_lib) + @gem_with_lib.expects(:require).with(@gem_with_lib.lib) + @gem_with_lib.add_load_paths + @gem_with_lib.load + assert @gem_with_lib.loaded? + end + + def test_gem_without_lib_loading + @gem_without_load.expects(:gem).with(@gem_without_load) + @gem_without_load.expects(:require).with(@gem_without_load.lib).never + @gem_without_load.add_load_paths + @gem_without_load.load + end + + def test_gem_dependencies_compare_for_uniq + gem1 = Rails::GemDependency.new "gem1" + gem1a = Rails::GemDependency.new "gem1" + gem2 = Rails::GemDependency.new "gem2" + gem2a = Rails::GemDependency.new "gem2" + gem3 = Rails::GemDependency.new "gem2", :version => ">=0.1" + gem3a = Rails::GemDependency.new "gem2", :version => ">=0.1" + gem4 = Rails::GemDependency.new "gem3" + gems = [gem1, gem2, gem1a, gem3, gem2a, gem4, gem3a, gem2, gem4] + assert_equal 4, gems.uniq.size + end + + def test_gem_load_frozen + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_A_VERSION + end + + def test_gem_load_frozen_specific_version + dummy_gem = Rails::GemDependency.new "dummy-gem-b", :version => '0.4.0' + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_B_VERSION + assert_equal '0.4.0', DUMMY_GEM_B_VERSION + end + + def test_gem_load_frozen_minimum_version + dummy_gem = Rails::GemDependency.new "dummy-gem-c", :version => '>=0.5.0' + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_C_VERSION + assert_equal '0.6.0', DUMMY_GEM_C_VERSION + end + + def test_gem_load_missing_specification + dummy_gem = Rails::GemDependency.new "dummy-gem-d" + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_D_VERSION + assert_equal '1.0.0', DUMMY_GEM_D_VERSION + assert_equal ['lib', 'lib/dummy-gem-d.rb'], dummy_gem.specification.files + end + + def test_gem_load_bad_specification + dummy_gem = Rails::GemDependency.new "dummy-gem-e", :version => "= 1.0.0" + dummy_gem.add_load_paths + dummy_gem.load + assert_not_nil DUMMY_GEM_E_VERSION + assert_equal '1.0.0', DUMMY_GEM_E_VERSION + end + + def test_gem_handle_missing_dependencies + dummy_gem = Rails::GemDependency.new "dummy-gem-g" + dummy_gem.add_load_paths + dummy_gem.load + assert_equal 1, dummy_gem.dependencies.size + assert_equal 1, dummy_gem.dependencies.first.dependencies.size + assert_nothing_raised do + dummy_gem.dependencies.each do |g| + g.dependencies + end + end + end + + def test_gem_ignores_development_dependencies + dummy_gem = Rails::GemDependency.new "dummy-gem-k" + dummy_gem.add_load_paths + dummy_gem.load + assert_equal 1, dummy_gem.dependencies.size + end + + def test_gem_guards_against_duplicate_unpacks + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.stubs(:frozen?).returns(true) + dummy_gem.expects(:unpack_base).never + dummy_gem.unpack + end + + def test_gem_does_not_unpack_framework_gems + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.stubs(:framework_gem?).returns(true) + dummy_gem.expects(:unpack_base).never + dummy_gem.unpack + end + + def test_gem_from_directory_name_attempts_to_load_specification + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1') + end + end + + def test_gem_from_directory_name + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1', false) + assert_equal 'dummy-gem', dummy_gem.name + assert_equal '= 1.1', dummy_gem.version_requirements.to_s + end + + def test_gem_from_directory_name_loads_specification_successfully + assert_nothing_raised do + dummy_gem = Rails::GemDependency.from_directory_name(File.join(Rails::GemDependency.unpacked_path, 'dummy-gem-g-1.0.0')) + assert_not_nil dummy_gem.specification + end + end + + def test_gem_from_invalid_directory_name + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem') + end + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy') + end + end + + def test_gem_determines_build_status + assert_equal true, Rails::GemDependency.new("dummy-gem-a").built? + assert_equal true, Rails::GemDependency.new("dummy-gem-i").built? + assert_equal false, Rails::GemDependency.new("dummy-gem-j").built? + end + + def test_gem_determines_build_status_only_on_vendor_gems + framework_gem = Rails::GemDependency.new('dummy-framework-gem') + framework_gem.stubs(:framework_gem?).returns(true) # already loaded + framework_gem.stubs(:vendor_rails?).returns(false) # but not in vendor/rails + framework_gem.stubs(:vendor_gem?).returns(false) # and not in vendor/gems + framework_gem.add_load_paths # freeze framework gem early + assert framework_gem.built? + end + + def test_gem_build_passes_options_to_dependencies + start_gem = Rails::GemDependency.new("dummy-gem-g") + dep_gem = Rails::GemDependency.new("dummy-gem-f") + start_gem.stubs(:dependencies).returns([dep_gem]) + dep_gem.expects(:build).with({ :force => true }).once + start_gem.build(:force => true) + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generator_lookup_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generator_lookup_test.rb new file mode 100644 index 00000000000..b650f304edf --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generator_lookup_test.rb @@ -0,0 +1,40 @@ +require 'plugin_test_helper' + +class GeneratorLookupTest < Test::Unit::TestCase + def setup + @fixture_dirs = %w{alternate default} + @configuration = Rails.configuration = Rails::Configuration.new + # We need to add our testing plugin directory to the plugin paths so + # the locator knows where to look for our plugins + @configuration.plugin_paths += @fixture_dirs.map{|fd| plugin_fixture_path(fd)} + @initializer = Rails::Initializer.new(@configuration) + @initializer.add_plugin_load_paths + @initializer.load_plugins + load 'rails_generator.rb' + require 'rails_generator/scripts' + end + + def test_should_load_from_all_plugin_paths + assert Rails::Generator::Base.lookup('a_generator') + assert Rails::Generator::Base.lookup('stubby_generator') + end + + def test_should_create_generator_source_for_each_directory_in_plugin_paths + sources = Rails::Generator::Base.sources + @fixture_dirs.each do |gen_dir| + expected_label = "plugins (fixtures/plugins/#{gen_dir})".to_sym + assert sources.any? {|source| source.label == expected_label } + end + end + + def test_should_preserve_order_in_usage_message + msg = Rails::Generator::Scripts::Base.new.send(:usage_message) + positions = @fixture_dirs.map do |gen_dir| + pos = msg.index("Plugins (fixtures/plugins/#{gen_dir})") + assert_not_nil pos + pos + end + assert_equal positions.sort, positions + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/generator_test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/generator_test_helper.rb new file mode 100644 index 00000000000..6f02eb381f8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/generator_test_helper.rb @@ -0,0 +1,310 @@ +require 'test/unit' +require 'fileutils' + +# Mock out what we need from AR::Base +module ActiveRecord + class Base + class << self + attr_accessor :pluralize_table_names, :timestamped_migrations + end + self.pluralize_table_names = true + self.timestamped_migrations = true + end + + module ConnectionAdapters + class Column + attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale + + def initialize(name, default, sql_type = nil) + @name = name + @default = default + @type = @sql_type = sql_type + end + + def human_name + @name.humanize + end + end + end +end + +# Mock up necessities from ActionView +module ActionView + module Helpers + module ActionRecordHelper; end + class InstanceTag; end + end +end + +# Set RAILS_ROOT appropriately fixture generation +tmp_dir = "#{File.dirname(__FILE__)}/../fixtures/tmp" + +if defined? RAILS_ROOT + RAILS_ROOT.replace tmp_dir +else + RAILS_ROOT = tmp_dir +end +FileUtils.mkdir_p RAILS_ROOT + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" +require 'initializer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +require 'rails_generator' + +class GeneratorTestCase < Test::Unit::TestCase + include FileUtils + + def setup + ActiveRecord::Base.pluralize_table_names = true + + mkdir_p "#{RAILS_ROOT}/app/views/layouts" + mkdir_p "#{RAILS_ROOT}/config" + mkdir_p "#{RAILS_ROOT}/db" + mkdir_p "#{RAILS_ROOT}/test/fixtures" + mkdir_p "#{RAILS_ROOT}/public/stylesheets" + + File.open("#{RAILS_ROOT}/config/routes.rb", 'w') do |f| + f << "ActionController::Routing::Routes.draw do |map|\n\nend" + end + end + + def teardown + rm_rf "#{RAILS_ROOT}/app" + rm_rf "#{RAILS_ROOT}/test" + rm_rf "#{RAILS_ROOT}/config" + rm_rf "#{RAILS_ROOT}/db" + rm_rf "#{RAILS_ROOT}/public" + end + + def test_truth + # don't complain, test/unit + end + + # Instantiates the Generator. + def build_generator(name, params) + Rails::Generator::Base.instance(name, params) + end + + # Runs the +create+ command (like the command line does). + def run_generator(name, params) + silence_generator do + build_generator(name, params).command(:create).invoke! + end + end + + # Silences the logger temporarily and returns the output as a String. + def silence_generator + logger_original = Rails::Generator::Base.logger + myout = StringIO.new + Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(myout) + yield if block_given? + Rails::Generator::Base.logger = logger_original + myout.string + end + + # Asserts that the given controller was generated. + # It takes a name or symbol without the _controller part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_controller_for(name, parent = "ApplicationController") + assert_generated_class "app/controllers/#{name.to_s.underscore}_controller", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given model was generated. + # It takes a name or symbol and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_model_for(name, parent = "ActiveRecord::Base") + assert_generated_class "app/models/#{name.to_s.underscore}", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given helper was generated. + # It takes a name or symbol without the _helper part. + # The contents of the module source file is passed to a block. + def assert_generated_helper_for(name) + assert_generated_module "app/helpers/#{name.to_s.underscore}_helper" do |body| + yield body if block_given? + end + end + + # Asserts that the given functional test was generated. + # It takes a name or symbol without the _controller_test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_functional_test_for(name, parent = "ActionController::TestCase") + assert_generated_class "test/functional/#{name.to_s.underscore}_controller_test",parent do |body| + yield body if block_given? + end + end + + # Asserts that the given helper test test was generated. + # It takes a name or symbol without the _helper_test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_helper_test_for(name, parent = "ActionView::TestCase") + path = "test/unit/helpers/#{name.to_s.underscore}_helper_test" + # Have to pass the path without the "test/" part so that class_name_from_path will return a correct result + class_name = class_name_from_path(path.gsub(/^test\//, '')) + + assert_generated_class path,parent,class_name do |body| + yield body if block_given? + end + end + + # Asserts that the given unit test was generated. + # It takes a name or symbol without the _test part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_unit_test_for(name, parent = "ActiveSupport::TestCase") + assert_generated_class "test/unit/#{name.to_s.underscore}_test", parent do |body| + yield body if block_given? + end + end + + # Asserts that the given file was generated. + # The contents of the file is passed to a block. + def assert_generated_file(path) + assert_file_exists(path) + File.open("#{RAILS_ROOT}/#{path}") do |f| + yield f.read if block_given? + end + end + + # asserts that the given file exists + def assert_file_exists(path) + assert File.exist?("#{RAILS_ROOT}/#{path}"), + "The file '#{RAILS_ROOT}/#{path}' should exist" + end + + # Asserts that the given class source file was generated. + # It takes a path without the .rb part and an optional super class. + # The contents of the class source file is passed to a block. + def assert_generated_class(path, parent = nil, class_name = class_name_from_path(path)) + assert_generated_file("#{path}.rb") do |body| + assert_match /class #{class_name}#{parent.nil? ? '':" < #{parent}"}/, body, "the file '#{path}.rb' should be a class" + yield body if block_given? + end + end + + def class_name_from_path(path) + # FIXME: Sucky way to detect namespaced classes + if path.split('/').size > 3 + path =~ /\/?(\d+_)?(\w+)\/(\w+)$/ + "#{$2.camelize}::#{$3.camelize}" + else + path =~ /\/?(\d+_)?(\w+)$/ + $2.camelize + end + end + + # Asserts that the given module source file was generated. + # It takes a path without the .rb part. + # The contents of the class source file is passed to a block. + def assert_generated_module(path) + # FIXME: Sucky way to detect namespaced modules + if path.split('/').size > 3 + path =~ /\/?(\w+)\/(\w+)$/ + module_name = "#{$1.camelize}::#{$2.camelize}" + else + path =~ /\/?(\w+)$/ + module_name = $1.camelize + end + + assert_generated_file("#{path}.rb") do |body| + assert_match /module #{module_name}/, body, "the file '#{path}.rb' should be a module" + yield body if block_given? + end + end + + # Asserts that the given CSS stylesheet file was generated. + # It takes a path without the .css part. + # The contents of the stylesheet source file is passed to a block. + def assert_generated_stylesheet(path) + assert_generated_file("public/stylesheets/#{path}.css") do |body| + yield body if block_given? + end + end + + # Asserts that the given YAML file was generated. + # It takes a path without the .yml part. + # The parsed YAML tree is passed to a block. + def assert_generated_yaml(path) + assert_generated_file("#{path}.yml") do |body| + yaml = YAML.load(body) + assert yaml, 'YAML data missing' + yield yaml if block_given? + end + end + + # Asserts that the given fixtures YAML file was generated. + # It takes a fixture name without the .yml part. + # The parsed YAML tree is passed to a block. + def assert_generated_fixtures_for(name) + assert_generated_yaml "test/fixtures/#{name.to_s.underscore}" do |yaml| + yield yaml if block_given? + end + end + + # Asserts that the given views were generated. + # It takes a controller name and a list of views (including extensions). + # The body of each view is passed to a block. + def assert_generated_views_for(name, *actions) + actions.each do |action| + assert_generated_file("app/views/#{name.to_s.underscore}/#{action}") do |body| + yield body if block_given? + end + end + end + + def assert_generated_migration(name, parent = "ActiveRecord::Migration") + file = Dir.glob("#{RAILS_ROOT}/db/migrate/*_#{name.to_s.underscore}.rb").first + assert !file.nil?, "should have generated the migration file but didn't" + + file = file.match(/db\/migrate\/[0-9]+_\w+/).to_s + assert_generated_class file, parent do |body| + assert_match /timestamps/, body, "should have timestamps defined" + yield body if block_given? + end + end + + # Asserts that the given migration file was not generated. + # It takes the name of the migration as a parameter. + def assert_skipped_migration(name) + migration_file = "#{RAILS_ROOT}/db/migrate/001_#{name.to_s.underscore}.rb" + assert !File.exist?(migration_file), "should not create migration #{migration_file}" + end + + # Asserts that the given resource was added to the routes. + def assert_added_route_for(name) + assert_generated_file("config/routes.rb") do |body| + assert_match /map.resources :#{name.to_s.underscore}/, body, + "should add route for :#{name.to_s.underscore}" + end + end + + # Asserts that the given methods are defined in the body. + # This does assume standard rails code conventions with regards to the source code. + # The body of each individual method is passed to a block. + def assert_has_method(body, *methods) + methods.each do |name| + assert body =~ /^ def #{name}(\(.+\))?\n((\n| .*\n)*) end/, "should have method #{name}" + yield(name, $2) if block_given? + end + end + + # Asserts that the given column is defined in the migration. + def assert_generated_column(body, name, type) + assert_match /t\.#{type.to_s} :#{name.to_s}/, body, "should have column #{name.to_s} defined" + end + + # Asserts that the given table is defined in the migration. + def assert_generated_table(body, name) + assert_match /create_table :#{name.to_s} do/, body, "should have table #{name.to_s} defined" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_controller_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_controller_generator_test.rb new file mode 100644 index 00000000000..43fbe972e2e --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_controller_generator_test.rb @@ -0,0 +1,44 @@ +require 'generators/generator_test_helper' + +module Admin +end + +class RailsControllerGeneratorTest < GeneratorTestCase + + def test_controller_generates_controller + run_generator('controller', %w(products)) + + assert_generated_controller_for :products + assert_generated_functional_test_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + end + + def test_controller_generates_namespaced_controller + run_generator('controller', %w(admin::products)) + + assert_generated_controller_for "admin::products" + assert_generated_functional_test_for "admin::products" + assert_generated_helper_for "admin::products" + assert_generated_helper_test_for "admin::products" + end + + def test_controller_generates_namespaced_and_not_namespaced_controllers + run_generator('controller', %w(products)) + + # We have to require the generated helper to show the problem because + # the test helpers just check for generated files and contents but + # do not actually load them. But they have to be loaded (as in a real environment) + # to make the second generator run fail + require "#{RAILS_ROOT}/app/helpers/products_helper" + + assert_nothing_raised do + begin + run_generator('controller', %w(admin::products)) + ensure + # cleanup + Object.send(:remove_const, :ProductsHelper) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_helper_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_helper_generator_test.rb new file mode 100644 index 00000000000..8d05f555e65 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_helper_generator_test.rb @@ -0,0 +1,36 @@ +require File.dirname(__FILE__) + '/generator_test_helper' + +class RailsHelperGeneratorTest < GeneratorTestCase + def test_helper_generates_helper + run_generator('helper', %w(products)) + + assert_generated_helper_for :products + assert_generated_helper_test_for :products + end + + def test_helper_generates_namespaced_helper + run_generator('helper', %w(admin::products)) + + assert_generated_helper_for "admin::products" + assert_generated_helper_test_for "admin::products" + end + + def test_helper_generates_namespaced_and_not_namespaced_helpers + run_generator('helper', %w(products)) + + # We have to require the generated helper to show the problem because + # the test helpers just check for generated files and contents but + # do not actually load them. But they have to be loaded (as in a real environment) + # to make the second generator run fail + require "#{RAILS_ROOT}/app/helpers/products_helper" + + assert_nothing_raised do + begin + run_generator('helper', %w(admin::products)) + ensure + # cleanup + Object.send(:remove_const, :ProductsHelper) + end + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_mailer_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_mailer_generator_test.rb new file mode 100644 index 00000000000..de61e6736d8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_mailer_generator_test.rb @@ -0,0 +1,29 @@ +require 'generators/generator_test_helper' + +class RailsMailerGeneratorTest < GeneratorTestCase + + def test_generates_mailer + run_generator('mailer', %w(Notifier reset_password)) + + assert_generated_model_for :notifier, 'ActionMailer::Base' do |model| + assert_has_method model, :reset_password do |name, body| + assert_equal [ + "subject 'Notifier#reset_password'", + "recipients ''", + "from ''", + "sent_on sent_at", + "", + "body :greeting => 'Hi,'" + ], + body.split("\n").map{|line| line.sub(' '*4, '') } + end + + assert_no_match /(self.default_url_options =|default_url_options\[.*\] =)/, model, + 'individual mailer models should not set default_url_options because the options are shared by all mailers' + end + + assert_generated_views_for :notifier, 'reset_password.erb' + assert_generated_unit_test_for :notifier, 'ActionMailer::TestCase' + assert_generated_file "test/fixtures/notifier/reset_password" + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_model_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_model_generator_test.rb new file mode 100644 index 00000000000..208b0c49224 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_model_generator_test.rb @@ -0,0 +1,96 @@ +require 'generators/generator_test_helper' + +class RailsModelGeneratorTest < GeneratorTestCase + + def test_model_generates_resources + run_generator('model', %w(Product name:string)) + + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_migration :create_products + end + + def test_model_skip_migration_skips_migration + run_generator('model', %w(Product name:string --skip-migration)) + + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_skipped_migration :create_products + end + + def test_model_with_attributes_generates_resources_with_attributes + run_generator('model', %w(Product name:string supplier_id:integer created_at:timestamp)) + + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_migration :create_products do |t| + assert_generated_column t, :name, :string + assert_generated_column t, :supplier_id, :integer + assert_generated_column t, :created_at, :timestamp + end + end + + def test_model_with_reference_attributes_generates_belongs_to_associations + run_generator('model', %w(Product name:string supplier:references)) + + assert_generated_model_for :product do |body| + assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" + end + end + + def test_model_with_belongs_to_attributes_generates_belongs_to_associations + run_generator('model', %w(Product name:string supplier:belongs_to)) + + assert_generated_model_for :product do |body| + assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" + end + end + + def test_migration_with_namespace + run_generator('model', %w(Gallery::Image)) + assert_generated_migration :gallery_images + assert_skipped_migration :create_images + end + + def test_migration_with_nested_namespace + run_generator('model', %w(Admin::Gallery::Image)) + assert_skipped_migration :create_images + assert_skipped_migration :create_gallery_images + + assert_generated_migration :admin_gallery_images do |t| + assert_generated_table t, :admin_gallery_images + end + end + + def test_migration_with_nested_namespace_without_pluralization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Admin::Gallery::Image)) + assert_skipped_migration :create_images + assert_skipped_migration :create_gallery_images + assert_skipped_migration :create_admin_gallery_images + assert_generated_migration :admin_gallery_image do |t| + assert_generated_table t, :admin_gallery_image + end + ensure + ActiveRecord::Base.pluralize_table_names = true + end + + def test_migration_with_namespaces_in_model_name_without_plurization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Gallery::Image)) + assert_generated_migration :create_gallery_image + assert_skipped_migration :create_gallery_images + ensure + ActiveRecord::Base.pluralize_table_names = true + end + + def test_migration_without_pluralization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Account)) + assert_generated_migration :create_account + assert_skipped_migration :create_accounts + ensure + ActiveRecord::Base.pluralize_table_names = true + end + +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_resource_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_resource_generator_test.rb new file mode 100644 index 00000000000..1f5bd0ef1e6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_resource_generator_test.rb @@ -0,0 +1,29 @@ +require 'generators/generator_test_helper' + +class RailsResourceGeneratorTest < GeneratorTestCase + def test_resource_generates_resources + run_generator('resource', %w(Product name:string)) + + assert_generated_controller_for :products + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_functional_test_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_migration :create_products + assert_added_route_for :products + end + + def test_resource_skip_migration_skips_migration + run_generator('resource', %w(Product name:string --skip-migration)) + + assert_generated_controller_for :products + assert_generated_model_for :product + assert_generated_fixtures_for :products + assert_generated_functional_test_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_skipped_migration :create_products + assert_added_route_for :products + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_scaffold_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_scaffold_generator_test.rb new file mode 100644 index 00000000000..2c60eb07d8d --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_scaffold_generator_test.rb @@ -0,0 +1,151 @@ +require 'generators/generator_test_helper' +require 'abstract_unit' + +class RailsScaffoldGeneratorTest < GeneratorTestCase + def test_scaffolded_names + g = Rails::Generator::Base.instance('scaffold', %w(ProductLine)) + assert_equal "ProductLines", g.controller_name + assert_equal "ProductLines", g.controller_class_name + assert_equal "ProductLine", g.controller_singular_name + assert_equal "product_lines", g.controller_plural_name + assert_equal "product_lines", g.controller_file_name + assert_equal "product_lines", g.controller_table_name + end + + def test_scaffold_generates_resources + + run_generator('scaffold', %w(Product name:string)) + + assert_generated_controller_for :products do |f| + + assert_has_method f, :index do |name, m| + assert_match /@products = Product\.all/, m, "#{name} should query products table" + end + + assert_has_method f, :show, :edit, :update, :destroy do |name, m| + assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table" + end + + assert_has_method f, :new do |name, m| + assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product" + end + + assert_has_method f, :create do |name, m| + assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product" + assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml" + end + + end + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb" + + assert_generated_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_skip_migration_skips_migration + run_generator('scaffold', %w(Product name:string --skip-migration)) + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_generates_resources_with_attributes + run_generator('scaffold', %w(Product name:string supplier_id:integer created_at:timestamp)) + + assert_generated_controller_for :products do |f| + + assert_has_method f, :index do |name, m| + assert_match /@products = Product\.all/, m, "#{name} should query products table" + end + + assert_has_method f, :show, :edit, :update, :destroy do |name, m| + assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table" + end + + assert_has_method f, :new do |name, m| + assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product" + end + + assert_has_method f, :create do |name, m| + assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product" + assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml" + end + + end + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb" + + assert_generated_migration :create_products do |t| + assert_generated_column t, :name, :string + assert_generated_column t, :supplier_id, :integer + assert_generated_column t, :created_at, :timestamp + end + + assert_added_route_for :products + end + + def test_scaffolded_plural_names + Rails::Generator::Base.logger.expects(:warning) + g = Rails::Generator::Base.instance('scaffold', %w(ProductLines)) + assert_equal "ProductLines", g.controller_name + assert_equal "ProductLines", g.controller_class_name + assert_equal "ProductLine", g.controller_singular_name + assert_equal "product_lines", g.controller_plural_name + assert_equal "product_lines", g.controller_file_name + assert_equal "product_lines", g.controller_table_name + assert_equal "ProductLine", g.class_name + end + + def test_scaffold_plural_model_name_without_force_plural_generates_singular_model + run_generator('scaffold', %w(Products name:string)) + + assert_generated_model_for :product + assert_generated_functional_test_for :products + assert_generated_unit_test_for :product + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end + + def test_scaffold_plural_model_name_with_force_plural_forces_plural_model + run_generator('scaffold', %w(Products name:string --force-plural)) + + assert_generated_model_for :products + assert_generated_functional_test_for :products + assert_generated_unit_test_for :products + assert_generated_fixtures_for :products + assert_generated_helper_for :products + assert_generated_helper_test_for :products + assert_generated_stylesheet :scaffold + assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" + assert_skipped_migration :create_products + assert_added_route_for :products + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_template_runner_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_template_runner_test.rb new file mode 100644 index 00000000000..2da6bd59b57 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/generators/rails_template_runner_test.rb @@ -0,0 +1,216 @@ +require 'abstract_unit' +require 'generators/generator_test_helper' + +class RailsTemplateRunnerTest < GeneratorTestCase + def setup + Rails::Generator::Base.use_application_sources! + run_generator('app', [RAILS_ROOT]) + # generate empty template + @template_path = File.join(RAILS_ROOT, 'template.rb') + File.open(File.join(@template_path), 'w') {|f| f << '' } + + @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git' + @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + end + + def teardown + super + rm_rf "#{RAILS_ROOT}/README" + rm_rf "#{RAILS_ROOT}/Rakefile" + rm_rf "#{RAILS_ROOT}/doc" + rm_rf "#{RAILS_ROOT}/lib" + rm_rf "#{RAILS_ROOT}/log" + rm_rf "#{RAILS_ROOT}/script" + rm_rf "#{RAILS_ROOT}/vendor" + rm_rf "#{RAILS_ROOT}/tmp" + rm_rf "#{RAILS_ROOT}/Capfile" + rm_rf @template_path + end + + def test_initialize_should_load_template + Rails::TemplateRunner.any_instance.expects(:load_template).with(@template_path) + silence_generator do + Rails::TemplateRunner.new(@template_path, RAILS_ROOT) + end + end + + def test_initialize_should_raise_error_on_missing_template_file + assert_raise(RuntimeError) do + silence_generator do + Rails::TemplateRunner.new('non/existent/path/to/template.rb', RAILS_ROOT) + end + end + end + + def test_file_should_write_data_to_file_path + run_template_method(:file, 'lib/test_file.rb', 'heres test data') + assert_generated_file_with_data 'lib/test_file.rb', 'heres test data' + end + + def test_file_should_write_block_contents_to_file_path + run_template_method(:file, 'lib/test_file.rb') { 'heres block data' } + assert_generated_file_with_data 'lib/test_file.rb', 'heres block data' + end + + def test_plugin_with_git_option_should_run_plugin_install + expects_run_ruby_script_with_command("script/plugin install #{@git_plugin_uri}") + run_template_method(:plugin, 'restful-authentication', :git => @git_plugin_uri) + end + + def test_plugin_with_svn_option_should_run_plugin_install + expects_run_ruby_script_with_command("script/plugin install #{@svn_plugin_uri}") + run_template_method(:plugin, 'restful-authentication', :svn => @svn_plugin_uri) + end + + def test_plugin_with_git_option_and_submodule_should_use_git_scm + Rails::Git.expects(:run).with("submodule add #{@git_plugin_uri} vendor/plugins/rest_auth") + run_template_method(:plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true) + end + + def test_plugin_with_no_options_should_skip_method + Rails::TemplateRunner.any_instance.expects(:run).never + run_template_method(:plugin, 'rest_auth', {}) + end + + def test_gem_should_put_gem_dependency_in_enviroment + run_template_method(:gem, 'will-paginate') + assert_rails_initializer_includes("config.gem 'will-paginate'") + end + + def test_gem_with_options_should_include_options_in_gem_dependency_in_environment + run_template_method(:gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com') + assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'") + end + + def test_gem_with_env_string_should_put_gem_dependency_in_specified_environment + run_template_method(:gem, 'rspec', :env => 'test') + assert_generated_file_with_data('config/environments/test.rb', "config.gem 'rspec'", 'test') + end + + def test_gem_with_env_array_should_put_gem_dependency_in_specified_environments + run_template_method(:gem, 'quietbacktrace', :env => %w[ development test ]) + assert_generated_file_with_data('config/environments/development.rb', "config.gem 'quietbacktrace'") + assert_generated_file_with_data('config/environments/test.rb', "config.gem 'quietbacktrace'") + end + + def test_gem_with_lib_option_set_to_false_should_put_gem_dependency_in_enviroment_correctly + run_template_method(:gem, 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com') + assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com'") + end + + def test_environment_should_include_data_in_environment_initializer_block + load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]' + run_template_method(:environment, load_paths) + assert_rails_initializer_includes(load_paths) + end + + def test_environment_with_block_should_include_block_contents_in_environment_initializer_block + run_template_method(:environment) do + '# This wont be added' + '# This will be added' + end + assert_rails_initializer_includes('# This will be added') + end + + def test_git_with_symbol_should_run_command_using_git_scm + Rails::Git.expects(:run).once.with('init') + run_template_method(:git, :init) + end + + def test_git_with_hash_should_run_each_command_using_git_scm + Rails::Git.expects(:run).times(2) + run_template_method(:git, {:init => '', :add => '.'}) + end + + def test_vendor_should_write_data_to_file_in_vendor + run_template_method(:vendor, 'vendor_file.rb', '# vendor data') + assert_generated_file_with_data('vendor/vendor_file.rb', '# vendor data') + end + + def test_lib_should_write_data_to_file_in_lib + run_template_method(:lib, 'my_library.rb', 'class MyLibrary') + assert_generated_file_with_data('lib/my_library.rb', 'class MyLibrary') + end + + def test_rakefile_should_write_date_to_file_in_lib_tasks + run_template_method(:rakefile, 'myapp.rake', 'task :run => [:environment]') + assert_generated_file_with_data('lib/tasks/myapp.rake', 'task :run => [:environment]') + end + + def test_initializer_should_write_date_to_file_in_config_initializers + run_template_method(:initializer, 'constants.rb', 'MY_CONSTANT = 42') + assert_generated_file_with_data('config/initializers/constants.rb', 'MY_CONSTANT = 42') + end + + def test_generate_should_run_script_generate_with_argument_and_options + expects_run_ruby_script_with_command('script/generate model MyModel') + run_template_method(:generate, 'model', 'MyModel') + end + + def test_rake_should_run_rake_command_with_development_env + expects_run_with_command('rake log:clear RAILS_ENV=development') + run_template_method(:rake, 'log:clear') + end + + def test_rake_with_env_option_should_run_rake_command_in_env + expects_run_with_command('rake log:clear RAILS_ENV=production') + run_template_method(:rake, 'log:clear', :env => 'production') + end + + def test_rake_with_sudo_option_should_run_rake_command_with_sudo + expects_run_with_command('sudo rake log:clear RAILS_ENV=development') + run_template_method(:rake, 'log:clear', :sudo => true) + end + + def test_capify_should_run_the_capify_command + expects_run_with_command('capify .') + run_template_method(:capify!) + end + + def test_freeze_should_freeze_rails_edge + expects_run_with_command('rake rails:freeze:edge') + run_template_method(:freeze!) + end + + def test_route_should_add_data_to_the_routes_block_in_config_routes + route_command = "map.route '/login', :controller => 'sessions', :action => 'new'" + run_template_method(:route, route_command) + assert_generated_file_with_data 'config/routes.rb', route_command + end + + def test_run_ruby_script_should_add_ruby_to_command_in_win32_environment + ruby_command = RUBY_PLATFORM =~ /win32/ ? 'ruby ' : '' + expects_run_with_command("#{ruby_command}script/generate model MyModel") + run_template_method(:generate, 'model', 'MyModel') + end + + protected + def run_template_method(method_name, *args, &block) + silence_generator do + @template_runner = Rails::TemplateRunner.new(@template_path, RAILS_ROOT) + @template_runner.send(method_name, *args, &block) + end + end + + def expects_run_with_command(command) + Rails::TemplateRunner.any_instance.stubs(:run).once.with(command, false) + end + + def expects_run_ruby_script_with_command(command) + Rails::TemplateRunner.any_instance.stubs(:run_ruby_script).once.with(command,false) + end + + def assert_rails_initializer_includes(data, message = nil) + message ||= "Rails::Initializer should include #{data}" + assert_generated_file 'config/environment.rb' do |body| + assert_match(/#{Regexp.escape("Rails::Initializer.run do |config|")}.+#{Regexp.escape(data)}.+end/m, body, message) + end + end + + def assert_generated_file_with_data(file, data, message = nil) + message ||= "#{file} should include '#{data}'" + assert_generated_file(file) do |file| + assert_match(/#{Regexp.escape(data)}/,file, message) + end + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/initializer_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/initializer_test.rb new file mode 100644 index 00000000000..68c99ee06b6 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/initializer_test.rb @@ -0,0 +1,416 @@ +require 'abstract_unit' +require 'initializer' + +require 'action_view' +require 'action_mailer' +require 'active_record' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +class ConfigurationMock < Rails::Configuration + attr_reader :environment_path + + def initialize(envpath) + super() + @environment_path = envpath + end +end + +class Initializer_load_environment_Test < Test::Unit::TestCase + def test_load_environment_with_constant + config = ConfigurationMock.new("#{File.dirname(__FILE__)}/fixtures/environment_with_constant.rb") + assert_nil $initialize_test_set_from_env + Rails::Initializer.run(:load_environment, config) + assert_equal "success", $initialize_test_set_from_env + ensure + $initialize_test_set_from_env = nil + end +end + +class Initializer_eager_loading_Test < Test::Unit::TestCase + def setup + @config = ConfigurationMock.new("") + @config.cache_classes = true + @config.load_paths = [File.expand_path(File.dirname(__FILE__) + "/fixtures/eager")] + @config.eager_load_paths = [File.expand_path(File.dirname(__FILE__) + "/fixtures/eager")] + @initializer = Rails::Initializer.new(@config) + @initializer.set_load_path + @initializer.set_autoload_paths + end + + def test_eager_loading_loads_parent_classes_before_children + assert_nothing_raised do + @initializer.load_application_classes + end + end +end + +class Initializer_after_initialize_with_blocks_environment_Test < Test::Unit::TestCase + def setup + config = ConfigurationMock.new("") + config.after_initialize do + $test_after_initialize_block1 = "success" + end + config.after_initialize do + $test_after_initialize_block2 = "congratulations" + end + assert_nil $test_after_initialize_block1 + assert_nil $test_after_initialize_block2 + + Rails::Initializer.any_instance.expects(:gems_dependencies_loaded).returns(true) + Rails::Initializer.run(:after_initialize, config) + end + + def teardown + $test_after_initialize_block1 = nil + $test_after_initialize_block2 = nil + end + + def test_should_have_called_the_first_after_initialize_block + assert_equal "success", $test_after_initialize_block1 + end + + def test_should_have_called_the_second_after_initialize_block + assert_equal "congratulations", $test_after_initialize_block2 + end +end + +class Initializer_after_initialize_with_no_block_environment_Test < Test::Unit::TestCase + def setup + config = ConfigurationMock.new("") + config.after_initialize do + $test_after_initialize_block1 = "success" + end + config.after_initialize # don't pass a block, this is what we're testing! + config.after_initialize do + $test_after_initialize_block2 = "congratulations" + end + assert_nil $test_after_initialize_block1 + + Rails::Initializer.any_instance.expects(:gems_dependencies_loaded).returns(true) + Rails::Initializer.run(:after_initialize, config) + end + + def teardown + $test_after_initialize_block1 = nil + $test_after_initialize_block2 = nil + end + + def test_should_have_called_the_first_after_initialize_block + assert_equal "success", $test_after_initialize_block1, "should still get set" + end + + def test_should_have_called_the_second_after_initialize_block + assert_equal "congratulations", $test_after_initialize_block2 + end +end + +class ConfigurationFrameworkPathsTests < Test::Unit::TestCase + def setup + @config = Rails::Configuration.new + @config.frameworks.clear + + File.stubs(:directory?).returns(true) + @config.stubs(:framework_root_path).returns('') + end + + def test_minimal + expected = %w( + /railties + /railties/lib + /activesupport/lib + ) + assert_equal expected, @config.framework_paths + end + + def test_actioncontroller_or_actionview_add_actionpack + @config.frameworks << :action_controller + assert_framework_path '/actionpack/lib' + + @config.frameworks = [:action_view] + assert_framework_path '/actionpack/lib' + end + + def test_paths_for_ar_ares_and_mailer + [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework| + @config.frameworks = [framework] + assert_framework_path "/#{framework.to_s.gsub('_', '')}/lib" + end + end + + def test_unknown_framework_raises_error + @config.frameworks << :action_foo + initializer = Rails::Initializer.new @config + initializer.expects(:require).raises(LoadError) + + assert_raise RuntimeError do + initializer.send :require_frameworks + end + end + + def test_action_mailer_load_paths_set_only_if_action_mailer_in_use + @config.frameworks = [:action_controller] + initializer = Rails::Initializer.new @config + initializer.send :require_frameworks + + assert_nothing_raised NameError do + initializer.send :load_view_paths + end + end + + def test_action_controller_load_paths_set_only_if_action_controller_in_use + @config.frameworks = [] + initializer = Rails::Initializer.new @config + initializer.send :require_frameworks + + assert_nothing_raised NameError do + initializer.send :load_view_paths + end + end + + protected + def assert_framework_path(path) + assert @config.framework_paths.include?(path), + "<#{path.inspect}> not found among <#{@config.framework_paths.inspect}>" + end +end + +require File.dirname(__FILE__) + '/plugin_test_helper' + +class InitializerPluginLoadingTests < Test::Unit::TestCase + def setup + @configuration = Rails::Configuration.new + @configuration.frameworks -= [:action_mailer] + @configuration.plugin_paths << plugin_fixture_root_path + @initializer = Rails::Initializer.new(@configuration) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + end + + def test_no_plugins_are_loaded_if_the_configuration_has_an_empty_plugin_list + only_load_the_following_plugins! [] + @initializer.load_plugins + assert_equal [], @initializer.loaded_plugins + end + + def test_only_the_specified_plugins_are_located_in_the_order_listed + plugin_names = [:plugin_with_no_lib_dir, :acts_as_chunky_bacon] + only_load_the_following_plugins! plugin_names + load_plugins! + assert_plugins plugin_names, @initializer.loaded_plugins + end + + def test_all_plugins_are_loaded_when_registered_plugin_list_is_untouched + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + load_plugins! + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @initializer.loaded_plugins, failure_tip + end + + def test_all_plugins_loaded_when_all_is_used + plugin_names = [:stubby, :acts_as_chunky_bacon, :all] + only_load_the_following_plugins! plugin_names + load_plugins! + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @initializer.loaded_plugins, failure_tip + end + + def test_all_plugins_loaded_after_all + plugin_names = [:stubby, :all, :acts_as_chunky_bacon] + only_load_the_following_plugins! plugin_names + load_plugins! + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @initializer.loaded_plugins, failure_tip + end + + def test_plugin_names_may_be_strings + plugin_names = ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir] + only_load_the_following_plugins! plugin_names + load_plugins! + failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + assert_plugins plugin_names, @initializer.loaded_plugins, failure_tip + end + + def test_registering_a_plugin_name_that_does_not_exist_raises_a_load_error + only_load_the_following_plugins! [:stubby, :acts_as_a_non_existant_plugin] + assert_raise(LoadError) do + load_plugins! + end + end + + def test_load_error_messages_mention_missing_plugins_and_no_others + valid_plugin_names = [:stubby, :acts_as_chunky_bacon] + invalid_plugin_names = [:non_existant_plugin1, :non_existant_plugin2] + only_load_the_following_plugins!( valid_plugin_names + invalid_plugin_names ) + begin + load_plugins! + flunk "Expected a LoadError but did not get one" + rescue LoadError => e + failure_tip = "It's likely someone renamed or deleted plugin fixtures without updating this test" + assert_plugins valid_plugin_names, @initializer.loaded_plugins, failure_tip + invalid_plugin_names.each do |plugin| + assert_match(/#{plugin.to_s}/, e.message, "LoadError message should mention plugin '#{plugin}'") + end + valid_plugin_names.each do |plugin| + assert_no_match(/#{plugin.to_s}/, e.message, "LoadError message should not mention '#{plugin}'") + end + + end + end + + def test_should_ensure_all_loaded_plugins_load_paths_are_added_to_the_load_path + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + + @initializer.add_plugin_load_paths + + assert $LOAD_PATH.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) + assert $LOAD_PATH.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) + end + + private + + def load_plugins! + @initializer.add_plugin_load_paths + @initializer.load_plugins + end +end + +class InitializerSetupI18nTests < Test::Unit::TestCase + def test_no_config_locales_dir_present_should_return_empty_load_path + File.stubs(:exist?).returns(false) + assert_equal [], Rails::Configuration.new.i18n.load_path + end + + def test_config_locales_dir_present_should_be_added_to_load_path + File.stubs(:exist?).returns(true) + Dir.stubs(:[]).returns([ "my/test/locale.yml" ]) + assert_equal [ "my/test/locale.yml" ], Rails::Configuration.new.i18n.load_path + end + + def test_config_defaults_should_be_added_with_config_settings + File.stubs(:exist?).returns(true) + Dir.stubs(:[]).returns([ "my/test/locale.yml" ]) + + config = Rails::Configuration.new + config.i18n.load_path << "my/other/locale.yml" + + assert_equal [ "my/test/locale.yml", "my/other/locale.yml" ], config.i18n.load_path + end + + def test_config_defaults_and_settings_should_be_added_to_i18n_defaults + File.stubs(:exist?).returns(true) + Dir.stubs(:[]).returns([ "my/test/locale.yml" ]) + + config = Rails::Configuration.new + config.i18n.load_path << "my/other/locale.yml" + + Rails::Initializer.run(:initialize_i18n, config) + assert_equal [ + File.expand_path(File.dirname(__FILE__) + "/../../activesupport/lib/active_support/locale/en.yml"), + File.expand_path(File.dirname(__FILE__) + "/../../actionpack/lib/action_view/locale/en.yml"), + File.expand_path(File.dirname(__FILE__) + "/../../activerecord/lib/active_record/locale/en.yml"), + # FIXME: should clean I18n.load_path between each test + File.expand_path(File.dirname(__FILE__) + "/../../railties/test/fixtures/plugins/engines/engine/config/locales/en.yml"), + "my/test/locale.yml", + "my/other/locale.yml" ], I18n.load_path.collect { |path| path =~ /^\./ ? File.expand_path(path) : path } + end + + def test_setting_another_default_locale + config = Rails::Configuration.new + config.i18n.default_locale = :de + Rails::Initializer.run(:initialize_i18n, config) + assert_equal :de, I18n.default_locale + end +end + +class InitializerDatabaseMiddlewareTest < Test::Unit::TestCase + def setup + @config = Rails::Configuration.new + @config.frameworks = [:active_record, :action_controller, :action_view] + end + + def test_initialize_database_middleware_doesnt_perform_anything_when_active_record_not_in_frameworks + @config.frameworks.clear + @config.expects(:middleware).never + Rails::Initializer.run(:initialize_database_middleware, @config) + end + + def test_database_middleware_initializes_when_session_store_is_active_record + store = ActionController::Base.session_store + ActionController::Base.session_store = ActiveRecord::SessionStore + + @config.middleware.expects(:insert_before).with(:"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:insert_before).with(:"ActiveRecord::SessionStore", ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + end + + def test_database_middleware_doesnt_initialize_when_session_store_is_not_active_record + store = ActionController::Base.session_store + ActionController::Base.session_store = ActionController::Session::CookieStore + + # Define the class, so we don't have to actually make it load + eval("class ActiveRecord::ConnectionAdapters::ConnectionManagement; end") + + @config.middleware.expects(:use).with(ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:use).with(ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + end + + def test_ensure_database_middleware_doesnt_use_action_controller_on_initializing + @config.frameworks -= [:action_controller] + store = ActionController::Base.session_store + ActionController::Base.session_store = ActiveRecord::SessionStore + + @config.middleware.expects(:use).with(ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:use).with(ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + @config.frameworks += [:action_controller] + end +end + +class InitializerViewPathsTest < Test::Unit::TestCase + def setup + @config = Rails::Configuration.new + @config.frameworks = [:action_view, :action_controller, :action_mailer] + + ActionController::Base.stubs(:view_paths).returns(stub) + ActionMailer::Base.stubs(:view_paths).returns(stub) + end + + def test_load_view_paths_doesnt_perform_anything_when_action_view_not_in_frameworks + @config.frameworks -= [:action_view] + ActionController::Base.view_paths.expects(:load!).never + ActionMailer::Base.view_paths.expects(:load!).never + Rails::Initializer.run(:load_view_paths, @config) + end + + def test_load_view_paths_loads_view_paths + ActionController::Base.view_paths.expects(:load!) + ActionMailer::Base.view_paths.expects(:load!) + Rails::Initializer.run(:load_view_paths, @config) + end +end + +class RailsRootTest < Test::Unit::TestCase + def test_rails_dot_root_equals_rails_root + assert_equal RAILS_ROOT, Rails.root.to_s + end + + def test_rails_dot_root_should_be_a_pathname + assert_equal File.join(RAILS_ROOT, 'app', 'controllers'), Rails.root.join('app', 'controllers').to_s + end +end + diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/metal_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/metal_test.rb new file mode 100644 index 00000000000..d3d231132b8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/metal_test.rb @@ -0,0 +1,72 @@ +require 'abstract_unit' +require 'initializer' + +class MetalTest < Test::Unit::TestCase + def test_metals_should_return_list_of_found_metal_apps + use_appdir("singlemetal") do + assert_equal(["FooMetal"], found_metals_as_string_array) + end + end + + def test_metals_should_respect_class_name_conventions + use_appdir("pluralmetal") do + assert_equal(["LegacyRoutes"], found_metals_as_string_array) + end + end + + def test_metals_should_return_alphabetical_list_of_found_metal_apps + use_appdir("multiplemetals") do + assert_equal(["MetalA", "MetalB"], found_metals_as_string_array) + end + end + + def test_metals_load_order_should_be_overriden_by_requested_metals + use_appdir("multiplemetals") do + Rails::Rack::Metal.requested_metals = ["MetalB", "MetalA"] + assert_equal(["MetalB", "MetalA"], found_metals_as_string_array) + end + end + + def test_metals_not_listed_should_not_load + use_appdir("multiplemetals") do + Rails::Rack::Metal.requested_metals = ["MetalB"] + assert_equal(["MetalB"], found_metals_as_string_array) + end + end + + def test_metal_finding_should_work_with_subfolders + use_appdir("subfolders") do + assert_equal(["Folder::MetalA", "Folder::MetalB"], found_metals_as_string_array) + end + end + + def test_metal_finding_with_requested_metals_should_work_with_subfolders + use_appdir("subfolders") do + Rails::Rack::Metal.requested_metals = ["Folder::MetalB"] + assert_equal(["Folder::MetalB"], found_metals_as_string_array) + end + end + + def test_metal_finding_should_work_with_multiple_metal_paths_in_185_and_below + use_appdir("singlemetal") do + engine_metal_path = "#{File.dirname(__FILE__)}/fixtures/plugins/engines/engine/app/metal" + Rails::Rack::Metal.metal_paths << engine_metal_path + $LOAD_PATH << engine_metal_path + assert_equal(["FooMetal", "EngineMetal"], found_metals_as_string_array) + end + end + + private + + def use_appdir(root) + dir = "#{File.dirname(__FILE__)}/fixtures/metal/#{root}" + Rails::Rack::Metal.metal_paths = ["#{dir}/app/metal"] + Rails::Rack::Metal.requested_metals = nil + $LOAD_PATH << "#{dir}/app/metal" + yield + end + + def found_metals_as_string_array + Rails::Rack::Metal.metals.map { |m| m.to_s } + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/mocks/routes.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/mocks/routes.rb new file mode 100644 index 00000000000..ea128636832 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/mocks/routes.rb @@ -0,0 +1,6 @@ +module ActionController + module Routing + class Routes + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_loader_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_loader_test.rb new file mode 100644 index 00000000000..b0ab4b77430 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_loader_test.rb @@ -0,0 +1,172 @@ +require 'plugin_test_helper' + +$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" +$:.unshift File.dirname(__FILE__) + "/../../actionmailer/lib" +require 'action_controller' +require 'action_mailer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +class TestPluginLoader < Test::Unit::TestCase + ORIGINAL_LOAD_PATH = $LOAD_PATH.dup + + def setup + reset_load_path! + + @configuration = Rails::Configuration.new + @configuration.plugin_paths << plugin_fixture_root_path + @initializer = Rails::Initializer.new(@configuration) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + + @failure_tip = "It's likely someone has added a new plugin fixture without updating this list" + + @loader = Rails::Plugin::Loader.new(@initializer) + end + + def test_should_locate_plugins_by_asking_each_locator_specifed_in_configuration_for_its_plugins_result + locator_1 = stub(:plugins => [:a, :b, :c]) + locator_2 = stub(:plugins => [:d, :e, :f]) + locator_class_1 = stub(:new => locator_1) + locator_class_2 = stub(:new => locator_2) + @configuration.plugin_locators = [locator_class_1, locator_class_2] + assert_equal [:a, :b, :c, :d, :e, :f], @loader.send(:locate_plugins) + end + + def test_should_memoize_the_result_of_locate_plugins_as_all_plugins + plugin_list = [:a, :b, :c] + @loader.expects(:locate_plugins).once.returns(plugin_list) + assert_equal plugin_list, @loader.all_plugins + assert_equal plugin_list, @loader.all_plugins # ensuring that locate_plugins isn't called again + end + + def test_should_return_empty_array_if_configuration_plugins_is_empty + @configuration.plugins = [] + assert_equal [], @loader.plugins + end + + def test_should_find_all_availble_plugins_and_return_as_all_plugins + assert_plugins [ :engine, :stubby, :plugin_with_no_lib_dir, :gemlike, :acts_as_chunky_bacon, :a], @loader.all_plugins.reverse, @failure_tip + end + + def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_untouched + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip + end + + def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_nil + @configuration.plugins = nil + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip + end + + def test_should_return_specific_plugins_named_in_config_plugins_array_if_set + plugin_names = [:acts_as_chunky_bacon, :stubby] + only_load_the_following_plugins! plugin_names + assert_plugins plugin_names, @loader.plugins + end + + def test_should_respect_the_order_of_plugins_given_in_configuration + plugin_names = [:stubby, :acts_as_chunky_bacon] + only_load_the_following_plugins! plugin_names + assert_plugins plugin_names, @loader.plugins + end + + def test_should_load_all_plugins_in_natural_order_when_all_is_used + only_load_the_following_plugins! [:all] + assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip + end + + def test_should_load_specified_plugins_in_order_and_then_all_remaining_plugins_when_all_is_used + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon, :all] + assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip + end + + def test_should_be_able_to_specify_loading_of_plugins_loaded_after_all + only_load_the_following_plugins! [:stubby, :all, :acts_as_chunky_bacon] + assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @loader.plugins, @failure_tip + end + + def test_should_accept_plugin_names_given_as_strings + only_load_the_following_plugins! ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir] + assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip + end + + def test_should_add_plugin_load_paths_to_global_LOAD_PATH_array + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + stubbed_application_lib_index_in_LOAD_PATHS = 4 + @loader.stubs(:application_lib_index).returns(stubbed_application_lib_index_in_LOAD_PATHS) + + @loader.add_plugin_load_paths + + assert $LOAD_PATH.index(File.join(plugin_fixture_path('default/stubby'), 'lib')) >= stubbed_application_lib_index_in_LOAD_PATHS + assert $LOAD_PATH.index(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) >= stubbed_application_lib_index_in_LOAD_PATHS + end + + def test_should_add_plugin_load_paths_to_Dependencies_load_paths + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + + @loader.add_plugin_load_paths + + assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) + assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) + end + + def test_should_add_engine_load_paths_to_Dependencies_load_paths + only_load_the_following_plugins! [:engine] + + @loader.add_plugin_load_paths + + %w( models controllers metal helpers ).each do |app_part| + assert ActiveSupport::Dependencies.load_paths.include?( + File.join(plugin_fixture_path('engines/engine'), 'app', app_part) + ), "Couldn't find #{app_part} in load path" + end + end + + def test_engine_controllers_and_action_mailers_should_have_their_view_path_set_when_loaded + only_load_the_following_plugins!([ :engine ]) + + @loader.send :add_engine_view_paths + + assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionController::Base.view_paths + assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionMailer::Base.view_paths + end + + def test_should_add_plugin_load_paths_to_Dependencies_load_once_paths + only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] + + @loader.add_plugin_load_paths + + assert ActiveSupport::Dependencies.load_once_paths.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) + assert ActiveSupport::Dependencies.load_once_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) + end + + def test_should_add_all_load_paths_from_a_plugin_to_LOAD_PATH_array + plugin_load_paths = ["a", "b"] + plugin = stub(:load_paths => plugin_load_paths) + @loader.stubs(:plugins).returns([plugin]) + + @loader.add_plugin_load_paths + + plugin_load_paths.each { |path| assert $LOAD_PATH.include?(path) } + end + + def test_should_add_locale_files_to_I18n_load_path + only_load_the_following_plugins! [:engine] + + @loader.send :add_engine_locales + + assert I18n.load_path.include?(File.join(plugin_fixture_path('engines/engine'), 'config', 'locales', 'en.yml')) + end + + + private + def reset_load_path! + $LOAD_PATH.clear + ORIGINAL_LOAD_PATH.each { |path| $LOAD_PATH << path } + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_locator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_locator_test.rb new file mode 100644 index 00000000000..471d9fc7c34 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_locator_test.rb @@ -0,0 +1,62 @@ +require 'plugin_test_helper' + +class PluginLocatorTest < Test::Unit::TestCase + def test_should_require_subclasses_to_implement_the_plugins_method + assert_raise(RuntimeError) do + Rails::Plugin::Locator.new(nil).plugins + end + end + + def test_should_iterator_over_plugins_returned_by_plugins_when_calling_each + locator = Rails::Plugin::Locator.new(nil) + locator.stubs(:plugins).returns([:a, :b, :c]) + plugin_consumer = mock + plugin_consumer.expects(:consume).with(:a) + plugin_consumer.expects(:consume).with(:b) + plugin_consumer.expects(:consume).with(:c) + + locator.each do |plugin| + plugin_consumer.consume(plugin) + end + end +end + +class PluginFileSystemLocatorTest < Test::Unit::TestCase + def setup + @configuration = Rails::Configuration.new + # We need to add our testing plugin directory to the plugin paths so + # the locator knows where to look for our plugins + @configuration.plugin_paths << plugin_fixture_root_path + @initializer = Rails::Initializer.new(@configuration) + @locator = Rails::Plugin::FileSystemLocator.new(@initializer) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + end + + def test_should_return_rails_plugin_instances_when_calling_create_plugin_with_a_valid_plugin_directory + assert_kind_of Rails::Plugin, @locator.send(:create_plugin, @valid_plugin_path) + end + + def test_should_return_nil_when_calling_create_plugin_with_an_invalid_plugin_directory + assert_nil @locator.send(:create_plugin, @empty_plugin_path) + end + + def test_should_return_all_plugins_found_under_the_set_plugin_paths + assert_equal ["a", "acts_as_chunky_bacon", "engine", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map(&:name).sort + end + + def test_should_find_plugins_only_under_the_plugin_paths_set_in_configuration + @configuration.plugin_paths = [File.join(plugin_fixture_root_path, "default")] + assert_equal ["acts_as_chunky_bacon", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map(&:name).sort + + @configuration.plugin_paths = [File.join(plugin_fixture_root_path, "alternate")] + assert_equal ["a"], @locator.plugins.map(&:name) + end + + def test_should_not_raise_any_error_and_return_no_plugins_if_the_plugin_path_value_does_not_exist + @configuration.plugin_paths = ["some_missing_directory"] + assert_nothing_raised do + assert @locator.plugins.empty? + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_test.rb new file mode 100644 index 00000000000..a6c390a45ab --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_test.rb @@ -0,0 +1,162 @@ +require 'plugin_test_helper' + +class PluginTest < Test::Unit::TestCase + def setup + @initializer = Rails::Initializer.new(Rails::Configuration.new) + @valid_plugin_path = plugin_fixture_path('default/stubby') + @empty_plugin_path = plugin_fixture_path('default/empty') + @gemlike_plugin_path = plugin_fixture_path('default/gemlike') + end + + def test_should_determine_plugin_name_from_the_directory_of_the_plugin + assert_equal 'stubby', plugin_for(@valid_plugin_path).name + assert_equal 'empty', plugin_for(@empty_plugin_path).name + end + + def test_should_not_be_loaded_when_created + assert !plugin_for(@valid_plugin_path).loaded? + end + + def test_should_be_marked_as_loaded_when_load_is_called + plugin = plugin_for(@valid_plugin_path) + assert !plugin.loaded? + plugin.stubs(:evaluate_init_rb) + assert_nothing_raised do + plugin.send(:load, anything) + end + assert plugin.loaded? + end + + def test_should_determine_validity_of_given_path + # This is a plugin path, with a lib dir + assert plugin_for(@valid_plugin_path).valid? + # This just has an init.rb and no lib dir + assert plugin_for(plugin_fixture_path('default/plugin_with_no_lib_dir')).valid? + # This would be a plugin path, but the directory is empty + assert !plugin_for(plugin_fixture_path('default/empty')).valid? + # This is a non sense path + assert !plugin_for(plugin_fixture_path('default/this_directory_does_not_exist')).valid? + end + + def test_should_return_empty_array_for_load_paths_when_plugin_has_no_lib_directory + assert_equal [], plugin_for(plugin_fixture_path('default/plugin_with_no_lib_dir')).load_paths + end + + def test_should_return_array_with_lib_path_for_load_paths_when_plugin_has_a_lib_directory + expected_lib_dir = File.join(plugin_fixture_path('default/stubby'), 'lib') + assert_equal [expected_lib_dir], plugin_for(plugin_fixture_path('default/stubby')).load_paths + end + + def test_should_raise_a_load_error_when_trying_to_determine_the_load_paths_from_an_invalid_plugin + assert_nothing_raised do + plugin_for(@valid_plugin_path).load_paths + end + + assert_raise(LoadError) do + plugin_for(@empty_plugin_path).load_paths + end + + assert_raise(LoadError) do + plugin_for('this_is_not_a_plugin_directory').load_paths + end + end + + def test_should_raise_a_load_error_when_trying_to_load_an_invalid_plugin + # This path is fine so nothing is raised + assert_nothing_raised do + plugin = plugin_for(@valid_plugin_path) + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + + # This path is fine so nothing is raised + assert_nothing_raised do + plugin = plugin_for(@gemlike_plugin_path) + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + + # This is an empty path so it raises + assert_raise(LoadError) do + plugin = plugin_for(@empty_plugin_path) + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + + assert_raise(LoadError) do + plugin = plugin_for('this_is_not_a_plugin_directory') + plugin.stubs(:evaluate_init_rb) + plugin.send(:load, @initializer) + end + end + + def test_should_raise_a_load_error_when_trying_to_access_load_paths_of_an_invalid_plugin + # This path is fine so nothing is raised + assert_nothing_raised do + plugin_for(@valid_plugin_path).load_paths + end + + # This is an empty path so it raises + assert_raise(LoadError) do + plugin_for(@empty_plugin_path).load_paths + end + + assert_raise(LoadError) do + plugin_for('this_is_not_a_plugin_directory').load_paths + end + end + + def test_loading_a_plugin_gives_the_init_file_access_to_all_it_needs + failure_tip = "Perhaps someone has written another test that loads this same plugin and therefore makes the StubbyMixin constant defined already." + assert !defined?(StubbyMixin), failure_tip + plugin = plugin_for(@valid_plugin_path) + plugin.load_paths.each { |path| $LOAD_PATH.unshift(path) } + # The init.rb of this plugin raises if it doesn't have access to all the things it needs + assert_nothing_raised do + plugin.load(@initializer) + end + assert defined?(StubbyMixin) + end + + def test_should_sort_naturally_by_name + a = plugin_for("path/a") + b = plugin_for("path/b") + z = plugin_for("path/z") + assert_equal [a, b, z], [b, z, a].sort + end + + def test_should_only_be_loaded_once + plugin = plugin_for(@valid_plugin_path) + assert !plugin.loaded? + plugin.expects(:evaluate_init_rb) + assert_nothing_raised do + plugin.send(:load, @initializer) + plugin.send(:load, @initializer) + end + assert plugin.loaded? + end + + def test_should_make_about_yml_available_as_about_method_on_plugin + plugin = plugin_for(@valid_plugin_path) + assert_equal "Plugin Author", plugin.about['author'] + assert_equal "1.0.0", plugin.about['version'] + end + + def test_should_return_empty_hash_for_about_if_about_yml_is_missing + assert_equal({}, plugin_for(about_yml_plugin_path('plugin_without_about_yaml')).about) + end + + def test_should_return_empty_hash_for_about_if_about_yml_is_malformed + assert_equal({}, plugin_for(about_yml_plugin_path('bad_about_yml')).about) + end + + private + + def about_yml_plugin_path(name) + File.join(File.dirname(__FILE__), 'fixtures', 'about_yml_plugins', name) + end + + def plugin_for(path) + Rails::Plugin.new(path) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_test_helper.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_test_helper.rb new file mode 100644 index 00000000000..f8c094d19f8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/plugin_test_helper.rb @@ -0,0 +1,29 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" +$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" + +require 'test/unit' +require 'active_support' +require 'initializer' +require File.join(File.dirname(__FILE__), 'abstract_unit') + +# We need to set RAILS_ROOT if it isn't already set +RAILS_ROOT = '.' unless defined?(RAILS_ROOT) + +class Test::Unit::TestCase + private + def plugin_fixture_root_path + File.join(File.dirname(__FILE__), 'fixtures', 'plugins') + end + + def only_load_the_following_plugins!(plugins) + @initializer.configuration.plugins = plugins + end + + def plugin_fixture_path(path) + File.join(plugin_fixture_root_path, path) + end + + def assert_plugins(list_of_names, array_of_plugins, message=nil) + assert_equal list_of_names.map(&:to_s), array_of_plugins.map(&:name), message + end +end \ No newline at end of file diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rack_static_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rack_static_test.rb new file mode 100644 index 00000000000..ad673f6f19a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rack_static_test.rb @@ -0,0 +1,46 @@ +require 'abstract_unit' + +require 'action_controller' +require 'rails/rack' + +class RackStaticTest < ActiveSupport::TestCase + def setup + FileUtils.cp_r "#{RAILS_ROOT}/fixtures/public", "#{RAILS_ROOT}/public" + end + + def teardown + FileUtils.rm_rf "#{RAILS_ROOT}/public" + end + + DummyApp = lambda { |env| + [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] + } + App = Rails::Rack::Static.new(DummyApp) + + test "serves dynamic content" do + assert_equal "Hello, World!", get("/nofile") + end + + test "serves static index at root" do + assert_equal "/index.html", get("/index.html") + assert_equal "/index.html", get("/index") + assert_equal "/index.html", get("/") + end + + test "serves static file in directory" do + assert_equal "/foo/bar.html", get("/foo/bar.html") + assert_equal "/foo/bar.html", get("/foo/bar/") + assert_equal "/foo/bar.html", get("/foo/bar") + end + + test "serves static index file in directory" do + assert_equal "/foo/index.html", get("/foo/index.html") + assert_equal "/foo/index.html", get("/foo/") + assert_equal "/foo/index.html", get("/foo") + end + + private + def get(path) + Rack::MockRequest.new(App).request("GET", path).body + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_generator_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_generator_test.rb new file mode 100644 index 00000000000..b2fc2f585de --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_generator_test.rb @@ -0,0 +1,145 @@ +require 'test/unit' + +# Optionally load RubyGems. +begin + require 'rubygems' +rescue LoadError +end + +# Mock out what we need from AR::Base. +module ActiveRecord + class Base + class << self + attr_accessor :pluralize_table_names + end + self.pluralize_table_names = true + end +end + +# And what we need from ActionView +module ActionView + module Helpers + module ActiveRecordHelper; end + class InstanceTag; end + end +end + + +# Must set before requiring generator libs. +if defined?(RAILS_ROOT) + RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures" +else + RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures" +end + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +require 'initializer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +require 'rails_generator' + +class RailsGeneratorTest < Test::Unit::TestCase + BUILTINS = %w(controller integration_test mailer migration model observer plugin resource scaffold session_migration) + CAPITALIZED_BUILTINS = BUILTINS.map { |b| b.capitalize } + + def setup + ActiveRecord::Base.pluralize_table_names = true + end + + def test_sources + expected = [:lib, :vendor, + "plugins (vendor/plugins)".to_sym, # /generators and /rails_generators + :user, + :RubyGems, :RubyGems, # gems named _generator, gems containing /rails_generator/ folder + :builtin] + expected.delete(:RubyGems) unless Object.const_defined?(:Gem) + assert_equal expected, Rails::Generator::Base.sources.map { |s| s.label } + end + + def test_lookup_builtins + (BUILTINS + CAPITALIZED_BUILTINS).each do |name| + assert_nothing_raised do + spec = Rails::Generator::Base.lookup(name) + assert_not_nil spec + assert_kind_of Rails::Generator::Spec, spec + + klass = spec.klass + assert klass < Rails::Generator::Base + assert_equal spec, klass.spec + end + end + end + + def test_autolookup + assert_nothing_raised { ControllerGenerator } + assert_nothing_raised { ModelGenerator } + end + + def test_lookup_missing_generator + assert_raise Rails::Generator::GeneratorError do + Rails::Generator::Base.lookup('missing').klass + end + end + + def test_lookup_missing_class + spec = nil + assert_nothing_raised { spec = Rails::Generator::Base.lookup('missing_class') } + assert_not_nil spec + assert_kind_of Rails::Generator::Spec, spec + assert_raise(NameError) { spec.klass } + end + + def test_generator_usage + (BUILTINS - ["session_migration"]).each do |name| + assert_raise(Rails::Generator::UsageError, "Generator '#{name}' should raise an error without arguments") { + Rails::Generator::Base.instance(name) + } + end + end + + def test_generator_spec + spec = Rails::Generator::Base.lookup('working') + assert_equal 'working', spec.name + assert_match(/#{spec.path}$/, "#{RAILS_ROOT}/lib/generators/working") + assert_equal :lib, spec.source + assert_nothing_raised { assert_match(/WorkingGenerator$/, spec.klass.name) } + end + + def test_named_generator_attributes + g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz)) + assert_equal 'admin/foo', g.name + assert_equal %w(admin), g.class_path + assert_equal 'Admin', g.class_nesting + assert_equal 'Admin::Foo', g.class_name + assert_equal 'foo', g.singular_name + assert_equal 'foos', g.plural_name + assert_equal g.singular_name, g.file_name + assert_equal "admin_#{g.plural_name}", g.table_name + assert_equal %w(bar baz), g.args + end + + def test_named_generator_attributes_without_pluralized + ActiveRecord::Base.pluralize_table_names = false + g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz)) + assert_equal "admin_#{g.singular_name}", g.table_name + end + + def test_session_migration_generator_with_pluralization + g = Rails::Generator::Base.instance('session_migration') + assert_equal 'session'.pluralize, g.send(:default_session_table_name) + ActiveRecord::Base.pluralize_table_names = false + assert_equal 'session', g.send(:default_session_table_name) + end + + def test_scaffold_controller_name + # Default behaviour is use the model name + g = Rails::Generator::Base.instance('scaffold', %w(Product)) + assert_equal "Products", g.controller_name + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_info_controller_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_info_controller_test.rb new file mode 100644 index 00000000000..e274e1aa6e1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_info_controller_test.rb @@ -0,0 +1,52 @@ +require 'abstract_unit' +require 'action_controller' +require 'action_controller/test_process' + +module Rails; end +require 'rails/info' +require 'rails/info_controller' + +class Rails::InfoController < ActionController::Base + @local_request = false + class << self + cattr_accessor :local_request + end + + # Re-raise errors caught by the controller. + def rescue_action(e) raise e end; + +protected + def local_request? + self.class.local_request + end +end + +ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' +end + +class Rails::InfoControllerTest < ActionController::TestCase + def setup + @controller = Rails::InfoController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + ActionController::Base.consider_all_requests_local = true + end + + def test_rails_info_properties_table_rendered_for_local_request + Rails::InfoController.local_request = true + get :properties + assert_tag :tag => 'table' + assert_response :success + end + + def test_rails_info_properties_error_rendered_for_non_local_request + Rails::InfoController.local_request = false + ActionController::Base.consider_all_requests_local = false + + get :properties + assert_tag :tag => 'p' + assert_response 500 + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_info_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_info_test.rb new file mode 100644 index 00000000000..9befd44a58b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/rails_info_test.rb @@ -0,0 +1,99 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" +$:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" +$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" + +require 'test/unit' +require 'active_support' + +unless defined?(Rails) && defined?(Rails::Info) + module Rails + class Info; end + end +end + +class InfoTest < Test::Unit::TestCase + def setup + Rails.send :remove_const, :Info + silence_warnings { load 'rails/info.rb' } + end + + def test_edge_rails_revision_not_set_when_svn_info_is_empty + Rails::Info.property 'Test that this will not be defined' do + Rails::Info.edge_rails_revision '' + end + assert !property_defined?('Test that this will not be defined') + end + + def test_edge_rails_revision_extracted_from_svn_info + Rails::Info.property 'Test Edge Rails revision' do + Rails::Info.edge_rails_revision <<-EOS + commit 420c4b3d8878156d04f45e47050ddc62ae00c68c + Author: David Heinemeier Hansson + Date: Sun Apr 13 17:33:27 2008 -0500 + + Added Rails.public_path to control where HTML and assets are expected to be loaded from +EOS + end + + assert_property 'Test Edge Rails revision', '420c4b3d8878156d04f45e47050ddc62ae00c68c' + end + + def test_property_with_block_swallows_exceptions_and_ignores_property + assert_nothing_raised do + Rails::Info.module_eval do + property('Bogus') {raise} + end + end + assert !property_defined?('Bogus') + end + + def test_property_with_string + Rails::Info.module_eval do + property 'Hello', 'World' + end + assert_property 'Hello', 'World' + end + + def test_property_with_block + Rails::Info.module_eval do + property('Goodbye') {'World'} + end + assert_property 'Goodbye', 'World' + end + + def test_framework_version + assert_property 'Active Support version', ActiveSupport::VERSION::STRING + end + + def test_frameworks_exist + Rails::Info.frameworks.each do |framework| + dir = File.dirname(__FILE__) + "/../../" + framework.gsub('_', '') + assert File.directory?(dir), "#{framework.classify} does not exist" + end + end + + protected + def svn_info=(info) + Rails::Info.module_eval do + class << self + def svn_info + info + end + end + end + end + + def properties + Rails::Info.properties + end + + def property_defined?(property_name) + properties.names.include? property_name + end + + def assert_property(property_name, value) + raise "Property #{property_name.inspect} not defined" unless + property_defined? property_name + assert_equal value, properties.value_for(property_name) + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/secret_key_generation_test.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/secret_key_generation_test.rb new file mode 100644 index 00000000000..2c7c3d5dfe2 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/secret_key_generation_test.rb @@ -0,0 +1,38 @@ +require 'abstract_unit' + +# Must set before requiring generator libs. +if defined?(RAILS_ROOT) + RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures" +else + RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures" +end + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" + +require 'initializer' + +# Mocks out the configuration +module Rails + def self.configuration + Rails::Configuration.new + end +end + +require 'rails_generator' +require 'rails_generator/secret_key_generator' +require 'rails_generator/generators/applications/app/app_generator' + +class SecretKeyGenerationTest < ActiveSupport::TestCase + SECRET_KEY_MIN_LENGTH = 128 + APP_NAME = "foo" + + def setup + @generator = Rails::SecretKeyGenerator.new(APP_NAME) + end + + def test_secret_key_generation + assert_deprecated /ActiveSupport::SecureRandom\.hex\(64\)/ do + assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH + end + end +end diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification new file mode 100644 index 00000000000..86dba2092c7 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-a +version: !ruby/object:Gem::Version + version: 0.4.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-a.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem A diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb new file mode 100644 index 00000000000..0453b38ab83 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb @@ -0,0 +1 @@ +DUMMY_GEM_A_VERSION="0.4.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification new file mode 100644 index 00000000000..5ea692d7a10 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-b +version: !ruby/object:Gem::Version + version: 0.4.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-b.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem B diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb new file mode 100644 index 00000000000..850b5dda839 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb @@ -0,0 +1 @@ +DUMMY_GEM_B_VERSION="0.4.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification new file mode 100644 index 00000000000..ab4707124af --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-b +version: !ruby/object:Gem::Version + version: 0.6.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-b.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem B diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb new file mode 100644 index 00000000000..7d6d01cd48f --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb @@ -0,0 +1 @@ +DUMMY_GEM_B_VERSION="0.6.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification new file mode 100644 index 00000000000..f90f60424ce --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-c +version: !ruby/object:Gem::Version + version: 0.4.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-c.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem C diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb new file mode 100644 index 00000000000..1a416bef82a --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb @@ -0,0 +1 @@ +DUMMY_GEM_C_VERSION="0.4.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification new file mode 100644 index 00000000000..e75c0aa66ac --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-c +version: !ruby/object:Gem::Version + version: 0.6.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-c.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem C diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb new file mode 100644 index 00000000000..9ba2ca8bb50 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb @@ -0,0 +1 @@ +DUMMY_GEM_C_VERSION="0.6.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb new file mode 100644 index 00000000000..e5cb007e5f8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-d-1.0.0/lib/dummy-gem-d.rb @@ -0,0 +1 @@ +DUMMY_GEM_D_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification new file mode 100644 index 00000000000..ce4443c8be3 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-e-1.0.0/.specification @@ -0,0 +1,28 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-e +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +files: +- lib +- lib/dummy-gem-e.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem E diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb new file mode 100644 index 00000000000..48bf91a7014 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-e-1.0.0/lib/dummy-gem-e.rb @@ -0,0 +1 @@ +DUMMY_GEM_E_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification new file mode 100644 index 00000000000..70a36b9a8c1 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-f-1.0.0/.specification @@ -0,0 +1,39 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-f +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: absolutely-no-such-gem + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-f.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem F diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb new file mode 100644 index 00000000000..0271c8c48a8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-f-1.0.0/lib/dummy-gem-f.rb @@ -0,0 +1 @@ +DUMMY_GEM_F_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification new file mode 100644 index 00000000000..27e29912a67 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-g-1.0.0/.specification @@ -0,0 +1,39 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-g +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-f + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-g.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb new file mode 100644 index 00000000000..8fc056586cd --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-g-1.0.0/lib/dummy-gem-g.rb @@ -0,0 +1 @@ +DUMMY_GEM_G_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification new file mode 100644 index 00000000000..b3f7930948b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification @@ -0,0 +1,29 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-h +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +files: +- lib +- lib/dummy-gem-h.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem H diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb new file mode 100644 index 00000000000..0f91234936b --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb @@ -0,0 +1 @@ +DUMMY_GEM_H_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification new file mode 100644 index 00000000000..50b4969da50 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification @@ -0,0 +1,41 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-i +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-i + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +extensions: +- ext/dummy-gem-i/extconf.rb +files: +- lib +- lib/dummy-gem-i.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb new file mode 100644 index 00000000000..2f9a376c2c8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb @@ -0,0 +1 @@ +DUMMY_GEM_I_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification new file mode 100644 index 00000000000..2c456546fc5 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification @@ -0,0 +1,41 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-j +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-j + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +extensions: +- ext/dummy-gem-j/extconf.rb +files: +- lib +- lib/dummy-gem-j.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb new file mode 100644 index 00000000000..8ecd363ff89 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb @@ -0,0 +1 @@ +DUMMY_GEM_J_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification new file mode 100644 index 00000000000..20edd0f8564 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification @@ -0,0 +1,49 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-k +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-k + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +- !ruby/object:Gem::Dependency + name: dummy-gem-h + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-k.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem I diff --git a/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb new file mode 100644 index 00000000000..97fb1d69ce8 --- /dev/null +++ b/crowbar/change-image/dell/openstack_manager/vendor/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb @@ -0,0 +1 @@ +DUMMY_GEM_K_VERSION="1.0.0" diff --git a/crowbar/change-image/dell/post-install.sh b/crowbar/change-image/dell/post-install.sh new file mode 100755 index 00000000000..4de5d69e686 --- /dev/null +++ b/crowbar/change-image/dell/post-install.sh @@ -0,0 +1,37 @@ +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# This script is called by the ubuntu.seed file during +# ubuntu install. It handles doing the manual sets needed to layout the +# filesystem the rest of the way before running the common_install.sh. +# +# The intent is that once the system is finished installing, the customer +# will edit the config.json file and run install.sh to complete installation +# and initial configuration. +# + +# +# We are a cd install. +# + +mkdir -p /target/tftpboot/ubuntu_dvd/dell +cp /cdrom/dell/common_install.sh /target/tftpboot/ubuntu_dvd/dell +cd /cdrom +/bin/tar -cf /target/tftpboot/ubuntu_dvd/dvd.tar . + +mount /proc /target/proc +chroot /target bash /tftpboot/ubuntu_dvd/dell/common_install.sh + diff --git a/crowbar/change-image/dell/rsyslog.d/10-crowbar-server.conf b/crowbar/change-image/dell/rsyslog.d/10-crowbar-server.conf new file mode 100644 index 00000000000..0d3dd407fdf --- /dev/null +++ b/crowbar/change-image/dell/rsyslog.d/10-crowbar-server.conf @@ -0,0 +1,2 @@ +$ModLoad imtcp +$InputTCPServerRun 514 diff --git a/crowbar/change-image/dell/single_chef_client.sh b/crowbar/change-image/dell/single_chef_client.sh new file mode 100755 index 00000000000..035df3ff28a --- /dev/null +++ b/crowbar/change-image/dell/single_chef_client.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +nohup /opt/dell/bin/looper_chef_client.sh & + diff --git a/crowbar/change-image/dell/sources.list b/crowbar/change-image/dell/sources.list new file mode 100644 index 00000000000..2b0a682e051 --- /dev/null +++ b/crowbar/change-image/dell/sources.list @@ -0,0 +1,71 @@ +# +# deb cdrom:[Ubuntu-Server 10.10 _Maverick Meerkat_ - Release amd64 (20101007)]/ maverick main restricted + +#deb cdrom:[Ubuntu-Server 10.10 _Maverick Meerkat_ - Release amd64 (20101007)]/ maverick main restricted +# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to +# +# deb cdrom:[Ubuntu-Server 10.10 _Maverick Meerkat_ - Release amd64 (20101007)]/ maverick main restricted + +#deb cdrom:[Ubuntu-Server 10.10 _Maverick Meerkat_ - Release amd64 (20101007)]/ maverick main restricted +# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to +# newer versions of the distribution. + +#deb http://us.archive.ubuntu.com/ubuntu/ maverick main restricted +#deb-src http://us.archive.ubuntu.com/ubuntu/ maverick main restricted + +## Major bug fix updates produced after the final release of the +## distribution. +#deb http://us.archive.ubuntu.com/ubuntu/ maverick-updates main restricted +#deb-src http://us.archive.ubuntu.com/ubuntu/ maverick-updates main restricted + +## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu +## team. Also, please note that software in universe WILL NOT receive any +## review or updates from the Ubuntu security team. +#deb http://us.archive.ubuntu.com/ubuntu/ maverick universe +#deb-src http://us.archive.ubuntu.com/ubuntu/ maverick universe +#deb http://us.archive.ubuntu.com/ubuntu/ maverick-updates universe +#deb-src http://us.archive.ubuntu.com/ubuntu/ maverick-updates universe + +## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu +## team, and may not be under a free licence. Please satisfy yourself as to +## your rights to use the software. Also, please note that software in +## multiverse WILL NOT receive any review or updates from the Ubuntu +## security team. +#deb http://us.archive.ubuntu.com/ubuntu/ maverick multiverse +#deb-src http://us.archive.ubuntu.com/ubuntu/ maverick multiverse +#deb http://us.archive.ubuntu.com/ubuntu/ maverick-updates multiverse +#deb-src http://us.archive.ubuntu.com/ubuntu/ maverick-updates multiverse + +## Uncomment the following two lines to add software from the 'backports' +## repository. +## N.B. software from this repository may not have been tested as +## extensively as that contained in the main release, although it includes +## newer versions of some applications which may provide useful features. +## Also, please note that software in backports WILL NOT receive any review +## or updates from the Ubuntu security team. +# deb http://us.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse +# deb-src http://us.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse + +## Uncomment the following two lines to add software from Canonical's +## 'partner' repository. +## This software is not part of Ubuntu, but is offered by Canonical and the +## respective vendors as a service to Ubuntu users. +# deb http://archive.canonical.com/ubuntu maverick partner +# deb-src http://archive.canonical.com/ubuntu maverick partner + +## Uncomment the following two lines to add software from Ubuntu's +## 'extras' repository. +## This software is not part of Ubuntu, but is offered by third-party +## developers who want to ship their latest software. +# deb http://extras.ubuntu.com/ubuntu maverick main +# deb-src http://extras.ubuntu.com/ubuntu maverick main + +#deb http://security.ubuntu.com/ubuntu maverick-security main restricted +#deb-src http://security.ubuntu.com/ubuntu maverick-security main restricted +#deb http://security.ubuntu.com/ubuntu maverick-security universe +#deb-src http://security.ubuntu.com/ubuntu maverick-security universe +#deb http://security.ubuntu.com/ubuntu maverick-security multiverse +#deb-src http://security.ubuntu.com/ubuntu maverick-security multiverse + +deb file:/tftpboot/ubuntu_dvd maverick main restricted +deb file:/tftpboot/ubuntu_dvd/extra / diff --git a/crowbar/change-image/dell/update_hostname.sh b/crowbar/change-image/dell/update_hostname.sh new file mode 100755 index 00000000000..09037aadcd5 --- /dev/null +++ b/crowbar/change-image/dell/update_hostname.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Assumes that this is run as root already + +FQDN=$1 +[[ $FQDN ]] || \ + FQDN=$(grep "cc_hostname_admin:" /etc/openstack/naming.conf | \ + sed "s/^[ ]*cc_hostname_admin:[ ]*//") + +HOSTNAME=${FQDN%%.*} +[[ $HOSTNAME == $FQDN ]] && HOSTNAME="" +DOMAINNAME=${FQDN#*.} + +grep -v "unassigned-hostname" /etc/hosts | \ + grep -v "redundant" /etc/hosts > /tmp/greg.out +mv /tmp/greg.out /etc/hosts +echo "127.0.1.1 $FQDN $HOSTNAME puppet" >> /etc/hosts +chown root.root /etc/hosts + +echo "$FQDN" > /etc/hostname +echo "$DOMAINNAME" > /etc/domainname + +hostname $FQDN + diff --git a/crowbar/change-image/dell/validate_bags.rb b/crowbar/change-image/dell/validate_bags.rb new file mode 100644 index 00000000000..b04497e5647 --- /dev/null +++ b/crowbar/change-image/dell/validate_bags.rb @@ -0,0 +1,46 @@ +#!/usr/bin/ruby + +require File.join(File.dirname(__FILE__), 'validate_data_bag' ) + +def verify_bags(base_dir) + err = false + Dir.chdir(base_dir) { |d| + Dir["*/*.json"].each { |bag| + schema = find_schema_for_file(bag) + puts "validating #{bag} against #{schema}" + begin + rc = validate(schema,bag) + rescue Exception => e + rc = -1 + end + err = true if rc != 0 + } + } + err +end + + +def find_schema_for_file(f) + dir = File.dirname(f) + base = File.basename(f,'.json') + components = base.split("-") + cnt = components.length + # trim sections of file name (-) to try to find schema + check_name ="" + begin + check_name = "#{dir}/#{components[0..cnt].join('-')}.schema" + break if File.exists?(check_name) + cnt = cnt - 1 + end while cnt >0 + check_name +end + + + +if __FILE__ == $0 + base_dir = ARGV[0].nil? ? "/opt/dell/chef/data_bags" : ARGV[0] + puts "Using #{base_dir}" + err = verify_bags( base_dir) + exit -3 if err + exit 0 +end \ No newline at end of file diff --git a/crowbar/change-image/dell/validate_data_bag.rb b/crowbar/change-image/dell/validate_data_bag.rb new file mode 100644 index 00000000000..7b641241311 --- /dev/null +++ b/crowbar/change-image/dell/validate_data_bag.rb @@ -0,0 +1,65 @@ +#!/usr/bin/ruby + +require 'kwalify' +require 'uri' + +require '/opt/dell/openstack_manager/app/models/crowbar_validator.rb' + +def validate(schema_filename, config_filename) + ## validate schema definition + metavalidator = Kwalify::MetaValidator.instance + parser = Kwalify::Yaml::Parser.new(metavalidator) + document = parser.parse_file(schema_filename) + + rc = 0 + + # show errors + for e in parser.errors + rc = -1 + puts "#{schema_filename}: #{e.linenum}:#{e.column} [#{e.path}] #{e.message}" + end if parser.errors && !parser.errors.empty? + + ## Validate the config file + validator = CrowbarValidator.new(schema_filename) + parser = Kwalify::Yaml::Parser.new(validator) + document = parser.parse_file(config_filename) + + ## show errors + for e in parser.errors + rc = -2 if rc == 0 + puts "#{config_filename}: #{e.linenum}:#{e.column} [#{e.path}] #{e.message}" + end if parser.errors && !parser.errors.empty? + + rc +end + +if __FILE__ == $0 + +if ARGV.size != 2 + puts "Must specify two files\n" + puts "Usage: validate_data_bag \n" + exit -3 +end + +if !File.exists?(ARGV[0]) + puts "#{ARGV[0]} must exist\n" + puts "Usage: validate_data_bag \n" + exit -4 +end + +if !File.exists?(ARGV[1]) + puts "#{ARGV[1]} must exist\n" + puts "Usage: validate_data_bag \n" + exit -5 +end + +rc = -1 +begin + rc = validate(ARGV[0], ARGV[1]) +rescue Exception => e + puts "Failed with exception: #{e.message}" +end + +exit rc + +end \ No newline at end of file diff --git a/crowbar/change-image/dell/validate_data_bags b/crowbar/change-image/dell/validate_data_bags new file mode 100755 index 00000000000..bbe4c7cdf1d --- /dev/null +++ b/crowbar/change-image/dell/validate_data_bags @@ -0,0 +1,16 @@ +#!/bin/bash + +ERC=0 +cd "${1:-/opt/dell/chef/data_bags}" + +for item in */*.json; do + /opt/dell/bin/validate_data_bag "${item%.json}.schema" $item + RC=$? + if [ $RC -ne 0 ] ; then + ERC=$RC + fi +done + +cd - 2>/dev/null >/dev/null + +exit $ERC diff --git a/crowbar/change-image/isolinux/isolinux.cfg b/crowbar/change-image/isolinux/isolinux.cfg new file mode 100644 index 00000000000..fe3b41c3276 --- /dev/null +++ b/crowbar/change-image/isolinux/isolinux.cfg @@ -0,0 +1,11 @@ +include menu.cfg +default OpenstackAdmin +prompt 0 +timeout 10 +gfxboot bootlogo + +LABEL OpenstackAdmin + menu label ^OpenStack Admin Installation + kernel /install/vmlinuz + append file=/cdrom/preseed/openstack_admin.seed debian-installer/locale=en_US.utf8 console-setup/layoutcode=us localechooser/translation/warn-light=true localechooser/translation/warn-severe=true cdrom-detect/load_floppy?=false cdrom-detect/load_media=false cdrom-detect/manual_config=true cdrom-detect/cdrom_module=none cdrom-detect/cdrom_device=/dev/scd0 initrd=/install/initrd.gz ramdisk_size=16384 root=/dev/ram rw quiet -- + diff --git a/crowbar/change-image/isolinux/pxelinux.0 b/crowbar/change-image/isolinux/pxelinux.0 new file mode 100644 index 00000000000..08270f1adab Binary files /dev/null and b/crowbar/change-image/isolinux/pxelinux.0 differ diff --git a/crowbar/change-image/isolinux/pxelinux.cfg/default b/crowbar/change-image/isolinux/pxelinux.cfg/default new file mode 100644 index 00000000000..be3b4e692ec --- /dev/null +++ b/crowbar/change-image/isolinux/pxelinux.cfg/default @@ -0,0 +1,12 @@ +include ../menu.cfg +default OpenStackAdmin +prompt 0 +timeout 10 +gfxboot bootlogo + +LABEL OpenStackAdmin + menu label ^OpenStack Admin installation + kernel ../install/netboot/ubuntu-installer/amd64/linux + append url=http://8.1.0.240:8091/ubuntu_dvd/preseed/net_ubuntu.seed debian-installer/locale=en_US.utf8 console-setup/layoutcode=us localechooser/translation/warn-light=true localechooser/translation/warn-severe=true netcfg/wireless_wep= netcfg/choose_interface=eth1 netcfg/dhcp_timeout=120 netcfg/get_hostname="redundant" initrd=../install/netboot/ubuntu-installer/amd64/initrd.gz ramdisk_size=16384 root=/dev/ram rw quiet -- + IPAPPEND 2 + diff --git a/crowbar/change-image/preseed/openstack_admin.seed b/crowbar/change-image/preseed/openstack_admin.seed new file mode 100755 index 00000000000..3c8df691655 --- /dev/null +++ b/crowbar/change-image/preseed/openstack_admin.seed @@ -0,0 +1,379 @@ +#### Contents of the preconfiguration file (for &releasename;) +### Localization +# Locale sets language and country. +d-i debian-installer/locale string en_US.utf8 + +# Keyboard selection. +# Disable automatic (interactive) keymap detection. +d-i console-setup/ask_detect boolean false +#d-i console-setup/modelcode string pc105 +d-i console-setup/layoutcode string us +# To select a variant of the selected layout (if you leave this out, the +# basic form of the layout will be used): +#d-i console-setup/variantcode string dvorak + +### Network configuration +# netcfg will choose an interface that has link if possible. This makes it +# skip displaying a list if there is more than one interface. +d-i netcfg/choose_interface select auto + +# To pick a particular interface instead: +#d-i netcfg/choose_interface select eth1 + +# If you have a slow dhcp server and the installer times out waiting for +# it, this might be useful. +d-i netcfg/dhcp_timeout string 3 + +# If you prefer to configure the network manually, uncomment this line and +# the static network configuration below. +#d-i netcfg/disable_dhcp boolean true + +# If you want the preconfiguration file to work on systems both with and +# without a dhcp server, uncomment these lines and the static network +# configuration below. +d-i netcfg/dhcp_failed note +d-i netcfg/dhcp_options select Do not configure the network at this time + +# Static network configuration. +#d-i netcfg/get_nameservers string 192.168.1.1 +#d-i netcfg/get_ipaddress string 192.168.1.42 +#d-i netcfg/get_netmask string 255.255.255.0 +#d-i netcfg/get_gateway string 192.168.1.1 +#d-i netcfg/confirm_static boolean true + +# Any hostname and domain names assigned from dhcp take precedence over +# values set here. However, setting the values still prevents the questions +# from being shown, even if values come from dhcp. +d-i netcfg/get_hostname string unassigned-hostname +d-i netcfg/get_domain string unassigned-domain + +# Disable that annoying WEP key dialog. +#d-i netcfg/wireless_wep string +# The wacky dhcp hostname that some ISPs use as a password of sorts. +#d-i netcfg/dhcp_hostname string radish + +# If non-free firmware is needed for the network or other hardware, you can +# configure the installer to always try to load it, without prompting. Or +# change to false to disable asking. +#d-i hw-detect/load_firmware boolean true + +### Mirror settings +# If you select ftp, the mirror/country string does not need to be set. +#d-i mirror/protocol string ftp +#d-i mirror/country string manual +#d-i mirror/http/hostname string http.us.debian.org +#d-i mirror/http/directory string /ubuntu +#d-i mirror/http/proxy string + +# Alternatively: by default, the installer uses CC.archive.ubuntu.com where +# CC is the ISO-3166-2 code for the selected country. You can preseed this +# so that it does so without asking. +#d-i mirror/http/mirror select CC.archive.ubuntu.com + +# Suite to install. +#d-i mirror/suite string &releasename; +# Suite to use for loading installer components (optional). +#d-i mirror/udeb/suite string &releasename; +# Components to use for loading installer components (optional). +#d-i mirror/udeb/components multiselect main, restricted + +### Clock and time zone setup +# Controls whether or not the hardware clock is set to UTC. +d-i clock-setup/utc boolean true + +# You may set this to any valid setting for $TZ; see the contents of +# /usr/share/zoneinfo/ for valid values. +d-i time/zone string US/Central + +# Controls whether to use NTP to set the clock during the install +#d-i clock-setup/ntp boolean true +# NTP server to use. The default is almost always fine here. +#d-i clock-setup/ntp-server string ntp.example.com + +### Partitioning +# If the system has free space you can choose to only partition that space. +# Alternatives: custom, some_device, some_device_crypto, some_device_lvm. +#d-i partman-auto/init_automatically_partition select biggest_free + +# Alternatively, you can specify a disk to partition. The device name must +# be given in traditional non-devfs format. +# Note: A disk must be specified, unless the system has only one disk. +# For example, to use the first SCSI/SATA hard disk: +d-i partman-auto/disk string /dev/sda +# In addition, you'll need to specify the method to use. +# The presently available methods are: "regular", "lvm" and "crypto" +d-i partman-auto/method string lvm + +# If one of the disks that are going to be automatically partitioned +# contains an old LVM configuration, the user will normally receive a +# warning. This can be preseeded away... +d-i partman-lvm/device_remove_lvm boolean true +# The same applies to pre-existing software RAID array: +d-i partman-md/device_remove_md boolean true +# And the same goes for the confirmation to write the lvm partitions. +d-i partman-lvm/confirm boolean true +d-i partman-lvm/confirm_nochanges boolean true +d-i partman-lvm/confirm_nooverwrite boolean true + +# For LVM partitioning, you can select how much of the volume group to use +# for logical volumes. +d-i partman-auto-lvm/guided_size string max +#d-i partman-auto-lvm/guided_size string 10GB +#d-i partman-auto-lvm/guided_size string 50% + +# You can choose one of the three predefined partitioning recipes: +# - atomic: all files in one partition +# - home: separate /home partition +# - multi: separate /home, /usr, /var, and /tmp partitions +d-i partman-auto/choose_recipe select atomic + +# Or provide a recipe of your own... +# The recipe format is documented in the file devel/partman-auto-recipe.txt. +# If you have a way to get a recipe file into the d-i environment, you can +# just point at it. +#d-i partman-auto/expert_recipe_file string /hd-media/recipe + +# If not, you can put an entire recipe into the preconfiguration file in one +# (logical) line. This example creates a small /boot partition, suitable +# swap, and uses the rest of the space for the root partition: +#d-i partman-auto/expert_recipe string \ +# boot-root :: \ +# 40 50 100 ext3 \ +# $primary{ } $bootable{ } \ +# method{ format } format{ } \ +# use_filesystem{ } filesystem{ ext3 } \ +# mountpoint{ /boot } \ +# . \ +# 500 10000 1000000000 ext3 \ +# method{ format } format{ } \ +# use_filesystem{ } filesystem{ ext3 } \ +# mountpoint{ / } \ +# . \ +# 64 512 300% linux-swap \ +# method{ swap } format{ } \ +# . + +# If you just want to change the default filesystem from ext3 to something +# else, you can do that without providing a full recipe. +#d-i partman/default_filesystem string ext4 + +# This makes partman automatically partition without confirmation, provided +# that you told it what to do using one of the methods above. +d-i partman/confirm_write_new_label boolean true +d-i partman/choose_partition select finish +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true + +### Controlling how partitions are mounted +# The default is to mount by UUID, but you can also choose "traditional" to +# use traditional device names, or "label" to try filesystem labels before +# falling back to UUIDs. +#d-i partman/mount_style select uuid + +### Base system installation +# The kernel image (meta) package to be installed; "none" can be used if no +# kernel is to be installed. +#d-i base-installer/kernel/image string linux-generic + +### Account setup +# Skip creation of a root account (normal user account will be able to +# use sudo). The default is false; preseed this to true if you want to set +# a root password. +#d-i passwd/root-login boolean false +# Alternatively, to skip creation of a normal user account. +#d-i passwd/make-user boolean false + +# Root password, either in clear text +#d-i passwd/root-password password r00tme +#d-i passwd/root-password-again password r00tme +# or encrypted using an MD5 hash. +#d-i passwd/root-password-crypted password [MD5 hash] + +# To create a normal user account. +d-i passwd/user-fullname string Admin Account +d-i passwd/username string openstack +# Normal user's password, either in clear text +d-i passwd/user-password password openstack +d-i passwd/user-password-again password openstack +# or encrypted using an MD5 hash. +#d-i passwd/user-password-crypted password [MD5 hash] +# Create the first user with the specified UID instead of the default. +#d-i passwd/user-uid string 1010 +# The installer will warn about weak passwords. If you are sure you know +# what you're doing and want to override it, uncomment this. +d-i user-setup/allow-password-weak boolean true + +# The user account will be added to some standard initial groups. To +# override that, use this. +#d-i passwd/user-default-groups string audio cdrom video + +# Set to true if you want to encrypt the first user's home directory. +d-i user-setup/encrypt-home boolean false + +### Apt setup +# You can choose to install restricted and universe software, or to install +# software from the backports repository. +#d-i apt-setup/restricted boolean true +#d-i apt-setup/universe boolean true +#d-i apt-setup/backports boolean true +# Uncomment this if you don't want to use a network mirror. +#d-i apt-setup/use_mirror boolean false +# Select which update services to use; define the mirrors to be used. +# Values shown below are the normal defaults. +#d-i apt-setup/services-select multiselect security +#d-i apt-setup/security_host string security.ubuntu.com +#d-i apt-setup/security_path string /ubuntu + +# Additional repositories, local[0-9] available +#d-i apt-setup/local0/repository string \ +# http://local.server/ubuntu &releasename; main +#d-i apt-setup/local0/comment string local server +# Enable deb-src lines +#d-i apt-setup/local0/source boolean true +# URL to the public key of the local repository; you must provide a key or +# apt will complain about the unauthenticated repository and so the +# sources.list line will be left commented out +#d-i apt-setup/local0/key string http://local.server/key + +# By default the installer requires that repositories be authenticated +# using a known gpg key. This setting can be used to disable that +# authentication. Warning: Insecure, not recommended. +#d-i debian-installer/allow_unauthenticated string true + +### Package selection +#tasksel tasksel/first multiselect ubuntu-desktop +tasksel tasksel/first multiselect ubuntu-standard, openssh-server +#tasksel tasksel/first multiselect kubuntu-desktop + +# Individual additional packages to install +d-i pkgsel/include string openssh-server curl +# Whether to upgrade packages after debootstrap. +# Allowed values: none, safe-upgrade, full-upgrade +#d-i pkgsel/upgrade select none + +# Language pack selection +#d-i pkgsel/language-packs multiselect de, en, zh + +# Policy for applying updates. May be "none" (no automatic updates), +# "unattended-upgrades" (install security updates automatically), or +# "landscape" (manage system with Landscape). +d-i pkgsel/update-policy select none + +# Some versions of the installer can report back on what software you have +# installed, and what software you use. The default is not to report back, +# but sending reports helps the project determine what software is most +# popular and include it on CDs. +#popularity-contest popularity-contest/participate boolean false + +# By default, the system's locate database will be updated after the +# installer has finished installing most packages. This may take a while, so +# if you don't want it, you can set this to "false" to turn it off. +#d-i pkgsel/updatedb boolean true + +### Boot loader installation +# Grub is the default boot loader (for x86). If you want lilo installed +# instead, uncomment this: +#d-i grub-installer/skip boolean true +# To also skip installing lilo, and install no bootloader, uncomment this +# too: +#d-i lilo-installer/skip boolean true + +# This is fairly safe to set, it makes grub install automatically to the MBR +# if no other operating system is detected on the machine. +d-i grub-installer/only_debian boolean true + +# This one makes grub-installer install to the MBR if it also finds some other +# OS, which is less safe as it might not be able to boot that other OS. +#d-i grub-installer/with_other_os boolean true + +# Alternatively, if you want to install to a location other than the mbr, +# uncomment and edit these lines: +#d-i grub-installer/only_debian boolean false +#d-i grub-installer/with_other_os boolean false +#d-i grub-installer/bootdev string (hd0,0) +# To install grub to multiple disks: +#d-i grub-installer/bootdev string (hd0,0) (hd1,0) (hd2,0) + +# Optional password for grub, either in clear text +#d-i grub-installer/password password r00tme +#d-i grub-installer/password-again password r00tme +# or encrypted using an MD5 hash, see grub-md5-crypt(8). +#d-i grub-installer/password-crypted password [MD5 hash] + +### Finishing up the installation +# During installations from serial console, the regular virtual consoles +# (VT1-VT6) are normally disabled in /etc/inittab. Uncomment the next +# line to prevent this. +#d-i finish-install/keep-consoles boolean true + +# Avoid that last message about the install being complete. +d-i finish-install/reboot_in_progress note + +# This will prevent the installer from ejecting the CD during the reboot, +# which is useful in some situations. +#d-i cdrom-detect/eject boolean false + +# This is how to make the installer shutdown when finished, but not +# reboot into the installed system. +#d-i debian-installer/exit/halt boolean true +# This will power off the machine instead of just halting it. +#d-i debian-installer/exit/poweroff boolean true + +### X configuration +# X can detect the right driver for some cards, but if you're preseeding, +# you override whatever it chooses. Still, vesa will work most places. +#xserver-xorg xserver-xorg/config/device/driver select vesa + +# A caveat with mouse autodetection is that if it fails, X will retry it +# over and over. So if it's preseeded to be done, there is a possibility of +# an infinite loop if the mouse is not autodetected. +#xserver-xorg xserver-xorg/autodetect_mouse boolean true + +# Monitor autodetection is recommended. +xserver-xorg xserver-xorg/autodetect_monitor boolean true +# Uncomment if you have an LCD display. +#xserver-xorg xserver-xorg/config/monitor/lcd boolean true +# X has three configuration paths for the monitor. Here's how to preseed +# the "medium" path, which is always available. The "simple" path may not +# be available, and the "advanced" path asks too many questions. +xserver-xorg xserver-xorg/config/monitor/selection-method \ + select medium +xserver-xorg xserver-xorg/config/monitor/mode-list \ + select 1024x768 @ 60 Hz + +### Preseeding other packages +# Depending on what software you choose to install, or if things go wrong +# during the installation process, it's possible that other questions may +# be asked. You can preseed those too, of course. To get a list of every +# possible question that could be asked during an install, do an +# installation, and then run these commands: +# debconf-get-selections --installer > file +# debconf-get-selections >> file + + +#### Advanced options +### Running custom commands during the installation +# d-i preseeding is inherently not secure. Nothing in the installer checks +# for attempts at buffer overflows or other exploits of the values of a +# preconfiguration file like this one. Only use preconfiguration files from +# trusted locations! To drive that home, and because it's generally useful, +# here's a way to run any shell command you'd like inside the installer, +# automatically. + +# This first command is run as early as possible, just after +# preseeding is read. +#d-i preseed/early_command string anna-install some-udeb + +# This command is run immediately before the partitioner starts. It may be +# useful to apply dynamic partitioner preseeding that depends on the state +# of the disks (which may not be visible when preseed/early_command runs). +#d-i partman/early_command string debconf-set partman-auto/disk "$(list-devices disk | head -n1)" + +# This command is run just before the install finishes, but when there is +# still a usable /target directory. You can chroot to /target and use it +# directly, or use the apt-install and in-target commands to easily install +# packages and run commands in the target system. +#d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh + +d-i preseed/late_command string /cdrom/dell/post-install.sh + diff --git a/crowbar/initrd/NOTES b/crowbar/initrd/NOTES new file mode 100755 index 00000000000..a7fd3a657e5 --- /dev/null +++ b/crowbar/initrd/NOTES @@ -0,0 +1,34 @@ +#!/bin/bash +#To fix the initrd to handle the USB CD timeout issue, do the following after expanding the ubuntu iso: + +# Expand the original initrd for the install image +sudo rm -rf initrd +sudo mkdir initrd + +# sigh -- for some bizzare reason, the bnx2.ko driver does not actually make +# it on to the initrd -- it is in a .udeb, so the installer will see it after +# the install image is mounted. Too bad if you wanted to mount the install +# image over nfs on a broadcom nic, though. Work around this by extracting +# all the nic drivers to the initrd. +sudo mkdir nicdrivers +cd nicdrivers +for udeb in ../../cd-image/pool/main/l/linux/nic-*-generic-*.udeb; do + sudo ar x "$udeb" + sudo tar xzf data.tar.gz + sudo rm debian-binary *.tar.gz +done + +cd ../initrd +gzip -dc ../../cd-image/install/initrd.gz | sudo cpio -id + +# Copy in fixed cdrom detect script with loop for cd rom timeouts +sudo cp ../cdrom-detect.postinst var/lib/dpkg/info/ +# Copy in all the nic drivers we should have had to begin with +sudo cp -a ../nicdrivers/lib . +sudo rm -rf ../nicdrivers + +# Build new initrd.gz +find . | sudo cpio --create --format='newc' | sudo gzip -9 > ../new-initrd.gz + +cd .. +sudo rm -rf initrd diff --git a/crowbar/initrd/cdrom-detect.postinst b/crowbar/initrd/cdrom-detect.postinst new file mode 100755 index 00000000000..e78b94e123a --- /dev/null +++ b/crowbar/initrd/cdrom-detect.postinst @@ -0,0 +1,249 @@ +#! /bin/sh + +set -e +. /usr/share/debconf/confmodule +#set -x + +log() { + logger -t cdrom-detect "$@" +} + +fail () { + log "CDROM-detect failed; unmounting CD just to be sure" + umount /cdrom 2>/dev/null || true + exit 1 +} + +# Is a cdrom already mounted? If so, assume it's the right one.. +mount | grep -q 'on /cdrom' && exit 0 +if [ -e /cdrom/.disk/info ]; then + CDNAME=`cat /cdrom/.disk/info` + log "Detected CD '$CDNAME'" + # This produces /var/log/installer/media-info on the installed + # system. + printf %s "$CDNAME" >/var/log/media-info + exit 0 +fi + +hw-detect cdrom-detect/detect_progress_title || true + +log "Searching for Ubuntu installation media..." + +mkdir /cdrom 2>/dev/null || true + +while true +do + mounted= + + if db_get cdrom-detect/try-usb && [ "$RET" = true ]; then + devices="$(list-devices usb-partition)" + devices="$devices $(list-devices mmc-partition)" + for device in $devices; do + if mount -t vfat -o ro,exec $device /cdrom && + [ -e /cdrom/.disk/info ]; then + log "CD-ROM mount succeeded: device=$device fstype=vfat" + mounted=1 + db_set cdrom-detect/cdrom_device $device + db_set cdrom-detect/cdrom_fs vfat + break + else + log "CD-ROM mount failed: device=$device fstype=vfat" + umount /cdrom 2>/dev/null || true + fi + done + fi + + if [ "$mounted" ]; then + break + fi + + wrong=0 + devices="$(list-devices cd; list-devices maybe-usb-floppy)" + sleep 20 + devices="$(list-devices cd; list-devices maybe-usb-floppy)" + for device in $devices; do + if mount -t iso9660 -o ro,exec $device /cdrom; then + log "CD-ROM mount succeeded: device=$device fstype=iso9660" + if [ -e /cdrom/.disk/info ]; then + CDNAME=`cat /cdrom/.disk/info` + log "Detected CD '$CDNAME'" + mounted=1 + db_set cdrom-detect/cdrom_device $device + db_set cdrom-detect/cdrom_fs iso9660 + break + else + log "The CD on $device is not an Ubuntu CD!" + wrong=1 + umount /cdrom 2>/dev/null || true + fi + else + log "CD-ROM mount failed: device=$device fstype=iso9660" + fi + done + + if [ "$mounted" = "1" ]; then + break + fi + + if [ "$wrong" = "1" ]; then + db_input critical cdrom-detect/wrong-cd || [ $? -eq 30 ] + db_go + fail + fi + + # If a device was detected but the mount failed, ask for the CD. + if [ -n "$devices" ]; then + db_input critical cdrom-detect/retry || [ $? -eq 30 ] + db_go + db_get cdrom-detect/retry + if [ "$RET" = "true" ]; then + log "Unmounting CD just to be sure" + umount /cdrom 2>/dev/null || true + continue + else + fail + fi + fi + + # If no device was detected, perhaps a driver floppy is needed. + if [ -e /usr/lib/debian-installer/retriever/media-retriever ]; then + db_input critical cdrom-detect/load_media + db_go + db_get cdrom-detect/load_media + if [ "$RET" = true ]; then + anna media-retriever + hw-detect cdrom-detect/detect_progress_title || true + continue + fi + fi + + # Otherwise manual configuration may be needed + db_input critical cdrom-detect/manual_config || [ $? -eq 30 ] + db_go + db_get cdrom-detect/manual_config + + modules=none + for i in `ls -1 /lib/modules/*/kernel/drivers/cdrom/ | sed 's/\.ko$//' | sed 's/\.o$//'`; do + modules="$modules, $i" + done + + if [ "$RET" = true ]; then + db_subst cdrom-detect/cdrom_module choices "$modules" + db_input critical cdrom-detect/cdrom_module || [ $? -eq 30 ] + db_go + + db_get cdrom-detect/cdrom_module + module="$RET" + + db_input critical cdrom-detect/cdrom_device || [ $? -eq 30 ] + db_go + + db_get cdrom-detect/cdrom_device + device="$RET" + + if [ "$module" != none ]; then + modprobe $module + fi + + if mount -t iso9660 -o ro,exec $device /cdrom; then + log "CD-ROM mount succeeded: device=$device fstype=iso9660" + db_set cdrom-detect/cdrom_fs iso9660 + mounted=1 + db_set cdrom-detect/cdrom_fs iso9660 + break + else + log "CD-ROM mount failed: device=$device fstype=iso9660" + fail + fi + else + fail + fi +done + +if [ -e /cdrom/.disk/info ]; then + CDNAME=`cat /cdrom/.disk/info` + log "Detected CD '$CDNAME'" +else + log "The available CD is not an Ubuntu CD!" + db_input critical cdrom-detect/wrong-cd || [ $? -eq 30 ] + db_go + fail +fi + +db_get cdrom-detect/cdrom_device +device="$RET" + +ARCH="`udpkg --print-architecture`" +if [ "$ARCH" = i386 ]; then + db_input low cdrom-detect/cdrom_hdparm || [ $? -eq 30 ] + db_go + db_get cdrom-detect/cdrom_hdparm + params="`printf '%s' "$RET" | sed 's/^ *//'`" + + if [ "$device" ] && [ "$params" ]; then + hdparm $params "$device" + apt-install hdparm || true + fi +fi + +# Get all the pool directories into the dentry cache, to cut down on seek +# times. +poolcount="$(set -- /cdrom/pool/*/*; echo $#)" +db_progress START 0 "$poolcount" cdrom-detect/scanning_progress_title +for pooldir in /cdrom/pool/*/*; do + if [ -d "$pooldir" ]; then + db_subst cdrom-detect/scanning_progress_step DIR "$pooldir" + db_progress INFO cdrom-detect/scanning_progress_step + log-output --pass-stdout -t cdrom-detect find "$pooldir/" >/dev/null || true + fi + db_progress STEP 1 +done +db_progress STOP + +# Set the suite and codename used by base-installer and base-config +# to the suite/codename that is on the CD. In case there are multiple +# suites, prefer the one in default-release. +for dir in $(cat /etc/default-release) $(ls -1 /cdrom/dists/); do + relfile="/cdrom/dists/$dir/Release" + if [ -e $relfile ]; then + suite=$(sed -n 's/^Suite: *//p' "$relfile") + codename=$(sed -n 's/^Codename: *//p' "$relfile") + log "Detected CD with '$suite' ($codename) distribution" + db_set cdrom/suite "$suite" + db_set cdrom/codename "$codename" + + break + fi +done + +if [ -z "$suite" ]; then + log "Error reading Release file; unable to determine distribution" + db_input critical cdrom-detect/no-release || [ $? -eq 30 ] + db_go + fail +fi + +# Install eject-udeb, to be able to use it in the finish-install script. +anna-install eject-udeb || true + +# This produces /var/log/installer/media-info on the installed system. +printf %s "$CDNAME" >/var/log/media-info + +# Hey, we're done +db_subst cdrom-detect/success cdname "$CDNAME" +db_input low cdrom-detect/success || [ $? -eq 30 ] +db_go + +anna-install apt-mirror-setup || true +if [ ! -e /cdrom/.disk/base_installable ]; then + log "Base system not installable from CD, requesting choose-mirror" + anna-install choose-mirror || true +else + anna-install apt-cdrom-setup || true + + # Install -support udeb (if available). + db_get cdrom/codename + anna-install $RET-support || true +fi + +exit 0 diff --git a/crowbar/test_framework/8086100e.rom b/crowbar/test_framework/8086100e.rom new file mode 100644 index 00000000000..47ce8f34445 Binary files /dev/null and b/crowbar/test_framework/8086100e.rom differ diff --git a/crowbar/test_framework/README.testing b/crowbar/test_framework/README.testing new file mode 100644 index 00000000000..1a206675a54 --- /dev/null +++ b/crowbar/test_framework/README.testing @@ -0,0 +1,536 @@ +This file documents the Crowbar smoketest framework + +Prerequisites: + * An x86_64 system with at least 16 GB of RAM, 8 cores, and 50 GB free + space in /home. The test framework deploys Crowbar to a set of + virtual machines, and you need a fairly beefy system to run them all. + * Bash 4 or higher. + The test framwork uses associative arrays to track the state of + the virtual machines it runs. + * cgroup support in the kernel. + The framework uses cgroups to ensure that it cleans up after itself + properly after a run. + * A dedicated user account for running tests in. + The framework expects to have a its own user account to store test + logs and hold the disk images. + * read/write access to /dev/kvm + The test framework runs its tests using KVM virtual machines. + * screen + The test framwork arranges to attach the KVM consoles to screen, and it + configures screen to capture all output from those consoles. + * brctl + The test framework arranges for the vms to talk over an isolated + network. + * ip tuntap + The test framework needs to set up and tear down tap interfaces, + and it relies on having a version of the ip command that includes + tuntap support. + * Two specific ruby gems: + * net-http-digest_auth + * json + Both of these gems should be installed systemwide on the test machine + using the gem install command before trying to run the framework. + * 192.168.124.0/24 + The admin node expects to live on 192.168.124.10, and the framework + needs to be able to talk to the admin node using IP. It expects + to be able to bind 192.168.124.1 to the first bridge it creates. + * Passwordless sudo to root for the following commands: + * /bin/mount, /bin/umount + The test framework needs to be able to mount and umount the Crowbar + image that it will test and the cgroup filesystem that it will track its + pids. + * /bin/ip + The test framework needs to be able to set up and tear down tap interfaces + for the KVM virtual machines. + * /usr/sbin/brctl + The test framework needs to be able to set up and tear down bridges for + the KVM virtual machines to talk over. + * $HOME/test_framework/make_cgroups.sh + make_cgroups.sh is the utility that we use to mount the cgroup filesystem + we track the pids we have spawned and that we use to kill all our + child processes when cleaning up. + +Usage: + +* Copy the test_framework directory and all its contents to $HOME + sudo uses absolute paths to determine whether a program can run under it, + and we need to run make_cgroups.sh as root to make cgroups for the overall + smoketest and for the individual test runs. +* Make sure the openstack iso you want to test is in $HOME. +* Make sure that all of the above prerequisites are met. +* Run $HOME/test_framework/test_crowbar.sh with the appropriate options. + If you run it with no options, it will try to smoketest a Nova deploy + and a Swift deploy in that order. + +Primary Operating Modes: + * run-test + This instructs the test framework to deploy a cluster, and then run + the specified tests with the specified options. + Example: $HOME/test_framework/test_crowbar.sh run-test scratch + * build-and-test + Does the same as run-test, but will try to create a new build of + the Crowbar deploy image first by invoking build_crowbar.sh first. + Example: $HOME/test_framework/test_crowbar.sh build-and-test scratch + * cleanup + This instructs the test framework to clean up as much as it can from + the previous run if it did not get a chance to perform its usual cleanup. + This will try to deallocate any stray tap interfaces, tear down bridges, + kill any stray processes, and umount any stray mounts we may have left + behind. + +Available Tests: + +By default, the framework knows how to perform 3 test runs: + * scratch + The scratch test just deploys a Crowbar cluster and brings all the nodes + up to the ready state. All other tests are logical extensions of it. + * nova + The nova test uses Crowbar to deploy glance and nova onto a cluster, + ensures that all the nova services launched correctly, launches a test + VM in Nova, and verifies that it can ping that test VM. + * swift + The swift test uses Crowbar to deploy swift onto a cluster, preps and + checks the Swift authentication layer, creates a non super user account, + and verifies that it can read and write a file to a directory on Swift. + +You can pass multiple tests to run-test and build-and-test. The test +framework will run them in order, and it will redeploy all the non-admin nodes +in between each test. + +Test Options: + * pause + Pause will cause the test framework to wait for a keypress after a + cluster test passes. + * pause-after-admin + Pause after admin will cause the test framework to wait for a keypress + after the admin node is deployed. + * develop-mode + Develop-mode will cause the test framework to wait for a keypress + whenever it is asked to reset the non-admin nodes or whenever we would + otherwise try to clean up after a test run. + +By itself, test_crowbar.sh only knows how to sanity check the admin node (which +it always does on every deploy) and perform a scratch test, which just deploys +a cluster without deploying anything else on that cluster. + +If it is asked to perform any other test, it looks for a directory named +$HOME/test_framework/_test, which should contain one +or more executable files with a .hook suffix. If it finds such a directory, +it will deploy the Crowbar cluster, and then run each .hook file in ascending +sort order. test_crowbar.sh will ensure that the crowbar CLI is available +in $PATH, that there is a valid username:password pair in $CROWBAR_KEY, that +passwordless SSH as root to all nodes in the cluster is functional for remote +command execution, and that the admin node is available at 192.168.124.10. +The hooks themselves are responsible for using Crowbar to create and deploy +proposals, verifying that the deploy was sucessful, and validating that the +deployed configuration is at least minimally functional. + +Usage Hints: + + * screen -r crowbar-test + The smoketest framework maintains a screen session named crowbar-test + that holds a shell session using the same environment the tests will run + with, a continually updated snaptshot of the state of the tests, and the + text-mode video console of all the virtual machines. + * (unset DISPLAY; $HOME/test_framework/test_crowbar.sh) + By default the test framework will let KVM use SDL video output if it + detects X. Unsetting DISPLAY before running test_crowbar.sh will force + it to not do that. + * xhost +local:; sudo su - crowbar-tester + Having to maintain two different X sessions to run things as crowbar-tester + (or whatever you want to name the user running the smoketests) is annoying. + xhost +local: will allow anyone connecting to the local Unix domain socket + for your X session to display applications. + +Directory Layout: + +The smoketest framework expects to be able to control the layout of its home +directory. It generates the following directory structure on its first run: + +$HOME + |- smoketest.log + |- testing + | |- cli + | | \- (the last copy of the Crowbar CLI that we extracted) + | |- openstack-being-tested.iso + | \- openstack-being-tested + | |- admin.disk + | |- admin.errlog + | |- admin.pid + | |- admin.status + | |- (repeat for the other virtual machines) + | |- image + | | \- (contents of openstack-being-tested.iso) + | \- logs + | |- admin.1 + | | |- ttyS0.log + | | \- ttyS1.log + | |- screenlog.admin.1 + | |- (repeat for each generation of each virtual machine) + | \- (log snapshots of the cluster at various checkpoints) + \- tested + |- openstack-archived-test-YYYYMMDD-HHMMSS-final_status.tar.gz + \- openstack-archived-test-YYYYMMDD-HHMMSS-final_status + \- (final snapshot of openstack-being-tested minus disk images) + +Each of the above files has a specific type of information: + * smoketest.log + The test_crowbar script always runs with set -x enabled using a + customized PS4 prompt. All output to stderr from the test script + that is not otherwise redirected ends up in smoketest.log. + * testing + The testing directory holds information pertaining to the Crowbar + deploy image that is currently being tested. If there is no image being + tested, this directory will only have the last instance of the + crowbar CLI that we extracted. + * cli + The cli directory holds the last copy of the Crowbar CLI that we extracted, + along with a couple of auxillary commands that the framework automatically + creates. The cli directory is added to $PATH so that the test hooks + can use the Crowbar CLI without needing to extract their own copy. + * openstack-being-tested.iso + This is the openstack.iso that is currently being tested. + The file does not actually get renamed, it will actually have whatever + name you told the build_crowbar.sh command to give it. + * openstack-being-tested + This directory holds all of the test information that the test framework + uses and captures on any single run of tests. + * admin.disk + This is the disk image for the admin node. There will be at least one + disk image for each virtual machine that is part of the test. + * admin.errlog + When the test framework deploys the admin node, it captures all stderr + output to this file. There will be an errlog file for each virtual machine + that the framework deploys. + * admin.pid + This is the PID of the KVM instance that is currently running the admin + node. The actual PID of any given virtual machine will change each time + a VM has to restart, so we have KVM always log the correct PID here. + There will be a pid file for each virtual machine. + * admin.status + This file is where the test framework tracks the status of the admin + node through a run of the test framework. There is a status file for + each virtual machine. + * image + This is where the openstack .iso we are currently testing is mounted. + * logs + This is where the logs from each individual KVM instance are captured to. + Since each virtual machine will be restarted at least once during a test + run, we capture the logs from KVM seperatly for each instance, and we group + the KVM runs into generations -- the first run if a given VM is generation + 1, the seconnd is generation 2, and so on. + * admin.1 + This is where the logs from the first generation of the admin VM + are saved. Each generation of each VM will have its own log directory. + * ttyS0.log + This is where anything that is output to ttyS0 from the VM is saved to. + The test framework configures syslog on each virtual machine to write + everything it captures to ttyS0, making this logfile a transcript + of everything logged to syslog. By default, Crowbar configures everything + that is capable of logging to syslog to do so. + * ttyS1.log + This is where anything written to ttyS1 in the VM is saved to. By default, + the test framework configures Crowbar to configure each machine it deploys + to use ttyS1 as a serial console, so everything written to the console + will be saved to this file. + * screenlog.admin.1 + By default, we configure KVM run under screen using the curses-based text + console. As long as a VM does not switch to graphics mode, everything that + the VM displays to the video hardware will be saved in this log. This + is useful for debugging VM failures that happen before the system + switches over to using the serial console. This logfile has lots of + ANSI escape sequences because it is an exact transcript of what KVM is + writing to the screen -- you should run it through strings if you want + to get easier-to-parse information from it. + * tested + This directory holds archives and directory trees from previous test runs. + The only thing we do not try to archive are the actual disk images, because + that would eat lots of space real fast unless you were saving the output + to as deduping store. + * openstack-archived-test-YYYYMMDD-HHMMSS-final_status.tar.gz + This is a tarball of the complete test run (minus disk images) that ended + on year-month-date hour:minute:second with the final status of final_status, + which can be one of Passed (all tests passed), Failed (one or more tests + failed), or Waiting (the framework terminated the test run due to a timeout + expiring before the tests could run). + * openstack-archived-test-YYYYMMDD-HHMMSS-final_status + This is a directory containing the contents of the above tarball for + easy on-machine perusal. + +In addition to the above files, the test framework will gather a complete +log and config snapshot of the cluster from Crowbar at the following points +in a test run: + * When the admin node indicates that it has deployed and the admin node + sanity tests have passed. + * When a test has completed, whether that test passed or failed + * Just before the virtual machines are killed at the end of a test run. + +These cluster snapshots are saved to the logs directory, have names in the +format of checkpoint_name-checkpoint-status-YYYYMMDD-HHMMSS.tar, and +have the following directory layout when untarred: + +YYYYMMDD-HHMMSS-five_random_characters + |- nodename + | \- nodename-YYYYMMDD-HHMMSS.tar.gz + \- (repeat for the rest of the nodes in the cluster that are up) + +The tarfile for each node contains all of /etc and /var/log. The tarfile for +the admin node also contains /opt/dell/openstack_manager/db (which is a dump +of the Crowbar-specific parts of the Chef database), and /install-logs (which +contains a capture of the logs the rest of the machines create in discovery +phase). + +Example Test Run Transcript: + +[crowbar-tester@testbox ~]$ (unset DISPLAY; test_crowbar.sh run-test scratch) +2011-07-16 11:32:29 -0500: admin - Mounting .iso +2011-07-16 11:32:29 -0500: admin - Hacking up kernel parameters +2011-07-16 11:32:29 -0500: admin - Creating disk image +2011-07-16 11:32:29 -0500: admin - Performing install from openstack-dev.iso +2011-07-16 11:39:09 -0500: admin - Node exited. +2011-07-16 11:39:09 -0500: admin - Deploying admin node crowbar tasks +2011-07-16 11:44:55 -0500: admin - admin.pod.cloud.openstack.org is discovered. +2011-07-16 11:47:01 -0500: admin - admin.pod.cloud.openstack.org is hardware-installing. +2011-07-16 11:47:11 -0500: admin - admin.pod.cloud.openstack.org is hardware-installed. +2011-07-16 11:47:32 -0500: admin - admin.pod.cloud.openstack.org is installing. +2011-07-16 11:47:42 -0500: admin - admin.pod.cloud.openstack.org is installed. +2011-07-16 11:47:52 -0500: admin - admin.pod.cloud.openstack.org is readying. +2011-07-16 11:48:02 -0500: admin - admin.pod.cloud.openstack.org is ready. +2011-07-16 11:48:02 -0500: admin - Daemonizing node with 1268 seconds left. +crowbar +crowbar_bios +crowbar_crowbar +crowbar_deployer +crowbar_dns +crowbar_ganglia +crowbar_glance +crowbar_hadoop +crowbar_logging +crowbar_machines +crowbar_nagios +crowbar_network +crowbar_node_state +crowbar_node_status +crowbar_nova +crowbar_ntp +crowbar_provisioner +crowbar_swift +crowbar_test +crowbar_watch_status +barclamp_lib.rb +barclamp_test.rb +blocking_chef_client.sh +gather_cli.sh +gather_logs.sh +install.sh +install_barclamp.sh +looper_chef_client.sh +single_chef_client.sh +transition.sh +update_hostname.sh +net-http-digest_auth-1.1.1.gem +PING 192.168.124.10 (192.168.124.10) 56(84) bytes of data. +64 bytes from 192.168.124.10: icmp_req=1 ttl=64 time=0.185 ms + +--- 192.168.124.10 ping statistics --- +1 packets transmitted, 1 received, 0% packet loss, time 0ms +rtt min/avg/max/mdev = 0.185/0.185/0.185/0.000 ms +apt_test: Success: empty list returned for / +apt_test: Success: empty list returned for /proposals +apt_test: Success: cli returned empty list for / +apt_test: Success: cli returned empty list for /proposals +apt_test: Success: create test_p1 proposal +apt_test: Success: create test_p1 proposal with bad url +apt_test: Success: list of proposals with test_p1 +apt_test: Success: getting test_p1 +apt_test: Success: editting test_p1 +apt_test: Success: failed to edit test_p1 +apt_test: Success: delete test_p1 proposal +apt_test: Success: not finding to delete test_p1 proposal +apt_test: Success: create test_p2 proposal +apt_test: Success: created committing test_p2 +apt_test: Success: list of proposals with test_p2 +apt_test: Success: list with test_p2 +apt_test: Success: getting test_p2 +apt_test: Success: delete test_p2 +apt_test: Success: delete missing test_p2 +apt_test: Success: delete test_p2 proposal +apt_test: Success: empty list returned for / +apt_test: Success: empty list returned for /proposals +apt_test: Success: cli returned empty list for / +apt_test: Success: cli returned empty list for /proposals +node_manipulate: Success: empty list returned for / +node_manipulate: Success: empty list returned for /proposals +node_manipulate: Success: cli returned empty list for / +node_manipulate: Success: cli returned empty list for /proposals +node_manipulate: Success transition dtest-machine-1.dell.com: result test +node_manipulate: Success transition dtest-machine-1.dell.com: name test +node_manipulate: Success: found dtest-machine-1.dell.com in node list +node_manipulate: Success: create test_p1 proposal +node_manipulate: Success creating proposal with test machine single +node_manipulate: Success creating proposal without test-multi-head +node_manipulate: Success creating proposal without test-multi-rest +node_manipulate: Success transition dtest-machine-2.dell.com: result test +node_manipulate: Success transition dtest-machine-2.dell.com: name test +node_manipulate: Success: found dtest-machine-2.dell.com in node list +node_manipulate: Success: queued committing test_p1 +node_manipulate: Success: not finding active test_p1 +node_manipulate: Success transition dtest-machine-1.dell.com: result test +node_manipulate: Success transition dtest-machine-1.dell.com: name test +node_manipulate: Success: found dtest-machine-1.dell.com in node list +node_manipulate: Success: found active test_p1 +node_manipulate: Success creating role with test machine single +node_manipulate: Success creating role without test-multi-head +node_manipulate: Success creating role without test-multi-rest +node_manipulate: Success: delete test_p1 proposal +node_manipulate: Success: delete test_p1 active +node_manipulate: Success: empty list returned for / +node_manipulate: Success: empty list returned for /proposals +node_manipulate: Success: cli returned empty list for / +node_manipulate: Success: cli returned empty list for /proposals +node_manipulate: Success: create test_p1 proposal +node_manipulate: Success creating role without test machine single +node_manipulate: Success creating proposal with test-multi-head +node_manipulate: Success creating proposal with test-multi-rest +node_manipulate: Success: delete test_p1 proposal +node_manipulate: Success: empty list returned for / +node_manipulate: Success: empty list returned for /proposals +node_manipulate: Success: cli returned empty list for / +node_manipulate: Success: cli returned empty list for /proposals +Deleted node[dtest-machine-1.dell.com]! +Deleted node[dtest-machine-2.dell.com]! +2011-07-16 11:51:33 -0500: admin - Admin deploy tests passed +2011-07-16 11:51:33 -0500: admin - Node deployed. +2011-07-16 11:51:33 -0500: Gathering admin-deployed logs, please wait. +2011-07-16 11:51:35 -0500: Done gathering admin-deployed logs. +2011-07-16 11:51:35 -0500: virt-1 - Creating disk image +2011-07-16 11:51:35 -0500: virt-2 - Creating disk image +2011-07-16 11:51:35 -0500: virt-3 - Creating disk image +2011-07-16 11:51:36 -0500: virt-4 - Creating disk image +2011-07-16 11:51:45 -0500: virt-1 - PXE booting node (0) +2011-07-16 11:51:45 -0500: virt-2 - PXE booting node (0) +2011-07-16 11:51:46 -0500: virt-3 - PXE booting node (0) +2011-07-16 11:51:46 -0500: virt-4 - PXE booting node (0) +2011-07-16 11:54:38 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:38 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:39 -0500: virt-3 - allocate d52-54-00-12-34-07.pod.cloud.openstack.org +2011-07-16 11:54:39 -0500: virt-2 - allocate d52-54-00-12-34-05.pod.cloud.openstack.org +2011-07-16 11:54:48 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:48 -0500: virt-1 - allocate d52-54-00-12-34-03.pod.cloud.openstack.org +2011-07-16 11:54:50 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:50 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:50 -0500: virt-3 - allocate d52-54-00-12-34-07.pod.cloud.openstack.org +2011-07-16 11:54:51 -0500: virt-2 - allocate d52-54-00-12-34-05.pod.cloud.openstack.org +2011-07-16 11:54:51 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:52 -0500: virt-4 - allocate d52-54-00-12-34-09.pod.cloud.openstack.org +2011-07-16 11:54:59 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is discovered. +2011-07-16 11:54:59 -0500: virt-1 - allocate d52-54-00-12-34-03.pod.cloud.openstack.org +2011-07-16 11:55:01 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is hardware-installing. +2011-07-16 11:55:01 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is discovered. +2011-07-16 11:55:01 -0500: virt-2 - allocate d52-54-00-12-34-05.pod.cloud.openstack.org +2011-07-16 11:55:02 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is hardware-installing. +2011-07-16 11:55:10 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is hardware-installing. +2011-07-16 11:55:11 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is hardware-installed. +2011-07-16 11:55:12 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is hardware-installing. +2011-07-16 11:55:12 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is hardware-installed. +2011-07-16 11:55:20 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is hardware-installed. +2011-07-16 11:55:22 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is hardware-installed. +2011-07-16 11:55:58 -0500: virt-4 - Node exited. +2011-07-16 11:55:58 -0500: virt-4 - PXE booting node (1) +2011-07-16 11:55:59 -0500: virt-3 - Node exited. +2011-07-16 11:55:59 -0500: virt-3 - PXE booting node (1) +2011-07-16 11:56:06 -0500: virt-1 - Node exited. +2011-07-16 11:56:06 -0500: virt-1 - PXE booting node (1) +2011-07-16 11:56:07 -0500: virt-2 - Node exited. +2011-07-16 11:56:07 -0500: virt-2 - PXE booting node (1) +2011-07-16 12:00:06 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is installing. +2011-07-16 12:00:06 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is installing. +2011-07-16 12:00:07 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is installing. +2011-07-16 12:00:07 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is installing. +2011-07-16 12:00:36 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is installed. +2011-07-16 12:00:37 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is installed. +2011-07-16 12:00:37 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is installed. +2011-07-16 12:00:38 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is installed. +2011-07-16 12:01:05 -0500: virt-4 - Node exited. +2011-07-16 12:01:06 -0500: virt-4 - Booting node to disk (2) +2011-07-16 12:01:09 -0500: virt-1 - Node exited. +2011-07-16 12:01:09 -0500: virt-1 - Booting node to disk (2) +2011-07-16 12:01:11 -0500: virt-2 - Node exited. +2011-07-16 12:01:11 -0500: virt-3 - Node exited. +2011-07-16 12:01:11 -0500: virt-2 - Booting node to disk (2) +2011-07-16 12:01:11 -0500: virt-3 - Booting node to disk (2) +2011-07-16 12:01:18 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is readying. +2011-07-16 12:01:28 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is readying. +2011-07-16 12:01:28 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is readying. +2011-07-16 12:01:29 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is readying. +2011-07-16 12:03:53 -0500: virt-4 - d52-54-00-12-34-09.pod.cloud.openstack.org is ready. +2011-07-16 12:03:53 -0500: virt-4 - Node deployed. +2011-07-16 12:03:53 -0500: virt-3 - d52-54-00-12-34-07.pod.cloud.openstack.org is ready. +2011-07-16 12:03:53 -0500: virt-3 - Node deployed. +2011-07-16 12:03:53 -0500: virt-1 - d52-54-00-12-34-03.pod.cloud.openstack.org is ready. +2011-07-16 12:03:53 -0500: virt-1 - Node deployed. +2011-07-16 12:04:13 -0500: virt-2 - d52-54-00-12-34-05.pod.cloud.openstack.org is ready. +2011-07-16 12:04:13 -0500: virt-2 - Node deployed. +2011-07-16 12:04:31 -0500: scratch deploy passed. +2011-07-16 12:04:41 -0500: scratch tests passed. +2011-07-16 12:04:41 -0500: Gathering scratch-tests-passed logs, please wait. +2011-07-16 12:04:46 -0500: Done gathering scratch-tests-passed logs. + +2011-07-16 12:04:46 -0500: virt-4 - Asking d52-54-00-12-34-09.pod.cloud.openstack.org to shut down +2011-07-16 12:04:47 -0500: virt-2 - Asking d52-54-00-12-34-05.pod.cloud.openstack.org to shut down +2011-07-16 12:04:48 -0500: virt-3 - Asking d52-54-00-12-34-07.pod.cloud.openstack.org to shut down +2011-07-16 12:04:49 -0500: virt-1 - Asking d52-54-00-12-34-03.pod.cloud.openstack.org to shut down +2011-07-16 12:04:49 -0500: Waiting for 2 minutes to allow nodes to power off +2011-07-16 12:04:50 -0500: virt-2 - Node exited. +2011-07-16 12:04:50 -0500: virt-3 - Node exited. +2011-07-16 12:04:51 -0500: virt-2 - Creating disk image +2011-07-16 12:04:51 -0500: virt-3 - Creating disk image +2011-07-16 12:05:02 -0500: virt-4 - Node exited. +2011-07-16 12:05:03 -0500: virt-4 - Creating disk image +2011-07-16 12:05:03 -0500: virt-1 - Node exited. +2011-07-16 12:05:04 -0500: virt-1 - Creating disk image + +2011-07-16 12:06:49 -0500: virt-4 - Forcing d52-54-00-12-34-09.pod.cloud.openstack.org to shut down +Deleted d52-54-00-12-34-09.pod.cloud.openstack.org +2011-07-16 12:06:49 -0500: virt-2 - Forcing d52-54-00-12-34-05.pod.cloud.openstack.org to shut down +Deleted d52-54-00-12-34-05.pod.cloud.openstack.org +2011-07-16 12:06:50 -0500: virt-3 - Forcing d52-54-00-12-34-07.pod.cloud.openstack.org to shut down +Deleted d52-54-00-12-34-07.pod.cloud.openstack.org +2011-07-16 12:06:50 -0500: virt-1 - Forcing d52-54-00-12-34-03.pod.cloud.openstack.org to shut down +Deleted d52-54-00-12-34-03.pod.cloud.openstack.org +2011-07-16 12:06:51 -0500: Gathering final logs, please wait. +2011-07-16 12:06:54 -0500: Done gathering final logs. +2011-07-16 12:06:55 -0500: admin - Killing (try 1, signal TERM) +2011-07-16 12:06:55 -0500: admin - Killed with SIGTERM +2011-07-16 12:06:56 -0500: virt-2 - Asked to be killed, giving up. +2011-07-16 12:06:57 -0500: virt-3 - Asked to be killed, giving up. +2011-07-16 12:06:58 -0500: virt-4 - Asked to be killed, giving up. +2011-07-16 12:07:01 -0500: virt-1 - Asked to be killed, giving up. +deploy_admin: Passed +scratch: Passed +Deploy Passed. +Logs are available at /home/crowbar-tester/tested/openstack-dev-20110716-120701-Passed.tar.gz. +[crowbar-tester@testbox ~]$ + + +From this, you can see the process the smoketest uses to set up Crowbar: + + * Mount the openstack .iso we will test on the host machine. + * Inject the parameters that the smoketest frawework needs to facilitate + automated testing (the default hostname, serial logging, serial console + settings, and the SSH public key of the user running the test). + * Install the admin node from the openstack ISO using the extra test + parameters + * Deploy Crowbar on the freshly installed admin node, cycling through the + normal node deploy phases as we go. + * Once the admin node is deployed, stop active status monitoring of it, + get a copy of the Crowbar CLI and install it on the host in testing/cli, + run some basic sanity tests and gather logs. + * Once the sanity tests have passed, spin up the rest of the virtual + machines and walk through their initial deploy process. + * Once the rest of the virtual machines have deployed, walk through the + tests we have asked the smoketest to run. In this instance, the test + framework was asked to run a scratch test, which just checks that it can + deploy nodes with just a basic OS install on them. If the smoketest + framweork was asked to test a nova or a swift deploy, it would have tried + to deploy that to the cluster as well. \ No newline at end of file diff --git a/crowbar/test_framework/admin_deployed/00-ping.hook b/crowbar/test_framework/admin_deployed/00-ping.hook new file mode 100755 index 00000000000..540a06a8226 --- /dev/null +++ b/crowbar/test_framework/admin_deployed/00-ping.hook @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +ping -c 1 "$CROWBAR_IP" \ No newline at end of file diff --git a/crowbar/test_framework/admin_deployed/01-crowbar-sanity-test.hook b/crowbar/test_framework/admin_deployed/01-crowbar-sanity-test.hook new file mode 100755 index 00000000000..db2cf5471d4 --- /dev/null +++ b/crowbar/test_framework/admin_deployed/01-crowbar-sanity-test.hook @@ -0,0 +1,19 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +exec barclamp_test.rb -t diff --git a/crowbar/test_framework/admin_deployed/README b/crowbar/test_framework/admin_deployed/README new file mode 100644 index 00000000000..e9ba84a2268 --- /dev/null +++ b/crowbar/test_framework/admin_deployed/README @@ -0,0 +1,5 @@ +Drop test hooks you want to run against the admin node before +the rest of the cluster nodes are brought up here. +They will get the IP of the admin node in the ADMIN_NODE_IP environment +variable, should have a numeric prefix and a suffix of .hook. +The hooks will be run in lexicographic sort order. \ No newline at end of file diff --git a/crowbar/test_framework/check_ready b/crowbar/test_framework/check_ready new file mode 100755 index 00000000000..6128988c420 --- /dev/null +++ b/crowbar/test_framework/check_ready @@ -0,0 +1,72 @@ +#!/usr/bin/ruby +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +require 'rubygems' +require 'json' +require 'net/http' +require 'net/http/digest_auth' + +# we only accept json, because we want to parse it. +@headers = {"Accept" => "application/json", + "Content-Type" => "application/json" } + +@username = "crowbar" +@password = "crowbar" + +def die(ret, msg) + STDERR.puts(msg) + exit(ret) +end + +def authenticate(req,uri,data=nil) + uri.user=@username + uri.password=@password + res=nil + begin + Net::HTTP.start(uri.host, uri.port) {|http| + r = req.new(uri.request_uri,@headers) + r.body = data if data + res = http.request r + if res['www-authenticate'] + digest_auth=Net::HTTP::DigestAuth.new + auth=Net::HTTP::DigestAuth.new.auth_header(uri, + res['www-authenticate'], + req::METHOD) + r.add_field 'Authorization', auth + res = http.request r + end + } + res + rescue + die(2,"Could not talk to Crowbar") + end +end + +node = ARGV[0] +admin_node=ENV["CROWBAR_IP"] +target_state=ENV["TARGET_STATE"] +admin_node = '192.168.124.10' if not admin_node +target_state = 'ready' unless target_state +conn = '' +res = authenticate(Net::HTTP::Get, + URI.parse("http://#{admin_node}:3000/crowbar/machines/1.0/show?name=#{node}")) +die(4,"Did not get good response from Crowbar") unless res.code == '200' +state = JSON.parse(res.body) +puts "#{node} is #{state["state"]}." +exit 0 if state["state"] == target_state +exit 1 diff --git a/crowbar/test_framework/make_cgroups.sh b/crowbar/test_framework/make_cgroups.sh new file mode 100755 index 00000000000..3411f3c48fc --- /dev/null +++ b/crowbar/test_framework/make_cgroups.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +set -e + +cgroups="$HOME/cgroups" +mount=true +{ + while read dev loc type opts rest; do + [[ $type = cgroup ]] || continue + cgroups=$loc + mount=false + break + done < /proc/self/mounts + + if [[ $mount = true ]]; then + mkdir -p "$cgroups" + mount -t cgroup -o cpu xxx "$cgroups" + fi + mkdir -p "$cgroups/$2" +} >&2 +echo "$1" >"$cgroups/$2/tasks" +echo "$cgroups/$2" \ No newline at end of file diff --git a/crowbar/test_framework/nova_test/02-deploy-glance.hook b/crowbar/test_framework/nova_test/02-deploy-glance.hook new file mode 100755 index 00000000000..c61d4ed139d --- /dev/null +++ b/crowbar/test_framework/nova_test/02-deploy-glance.hook @@ -0,0 +1,31 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +cd "$HOME/testing/cli" +echo "Deploying glance" +if crowbar glance proposal create cluster1 && \ + crowbar glance proposal commit cluster1; then + echo "Glance deployed." + crowbar glance show cluster1 >"$LOGDIR/glance.json" + exit 0 +else + echo "Glance failed to deploy." + crowbar glance proposal show cluster1 >"$LOGDIR/glance-failed.json" + exit 1 +fi + \ No newline at end of file diff --git a/crowbar/test_framework/nova_test/03-deploy-nova.hook b/crowbar/test_framework/nova_test/03-deploy-nova.hook new file mode 100755 index 00000000000..0115415cbf3 --- /dev/null +++ b/crowbar/test_framework/nova_test/03-deploy-nova.hook @@ -0,0 +1,136 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +[[ $DEBUG ]] && set -x + +cd "$HOME/testing/cli" + +die() { res=$1; shift; echo "$@"; exit $res; } + +nova_do() { + # $1 = address of nova controller + # $@ = command to run as the nova user. + local address="$1" + shift + [[ $1 ]] || return 1 + run_on "$address" sudo -u nova -H -i "$@" +} + +echo "Deploying Nova..." +crowbar nova proposal create cluster1 +crowbar nova proposal show cluster1 >"$LOGDIR/nova-default.json" +sed '/libvirt_type/ s/kvm/qemu/' < "$LOGDIR/nova-default.json" \ + > "$LOGDIR/nova-qemu.json" +crowbar nova --file "$LOGDIR/nova-qemu.json" proposal edit cluster1 +if ! crowbar nova proposal commit cluster1; then + echo "Nova failed to deploy." + crowbar nova proposal show cluster1 >"$LOGDIR/nova-failed.json" + exit 1 +fi +echo "Nova deployed. Finding controller..." +read controller < <(crowbar nova show cluster1 |sed -n '/"nova-multi-controller": \[/ { n; p }') +controller="${controller//\"/}." +addr_re='Address: ([0-9.]+)' +if [[ ! $(nslookup "$controller" "$CROWBAR_IP") =~ $addr_re ]]; then + echo "Could not find IP address of the nova controller ($controller)" + crowbar nova show cluster1 >"$LOGDIR/nova-failed.json" + exit 1 +fi +nova_ip="${BASH_REMATCH[1]}" +echo "Controller at $nova_ip. Verifying nova services..." + +while read node node_type node_enabled node_status rest; do + case $node_type in + nova-network) [[ $node_enabled = enabled && \ + $node_status = ':-)' ]] && nova_network=true;; + nova-compute) [[ $node_enabled = enabled && \ + $node_status = ':-)' ]] && nova_compute=true;; + nova-scheduler) [[ $node_enabled = enabled && \ + $node_status = ':-)' ]] && nova_scheduler=true;; + esac +done < <(nova_do "$nova_ip" nova-manage service list |tee \ + "$LOGDIR/nova-services.status") +if ! [[ $nova_network && $nova_compute && $nova_scheduler ]]; then + echo "Nova services do not report as healthy!" + exit 1 +fi +echo "Nova services healthy. Finding test image to launch..." +test_image='' +ami_re='(ami-[0-9]+)' +while read line;do + [[ $line =~ $ami_re ]] || continue + img_id="${BASH_REMATCH[1]}" + [[ $line =~ available && $line =~ public ]] || continue + test_image=$img_id + break +done < <(nova_do "$nova_ip" euca-describe-images |tee \ + "$LOGDIR/nova-images.status") +if [[ ! $test_image ]]; then + echo "Could not find a test image to run on Nova" + exit 1 +fi +echo "Launching $test_image..." +nova_do "$nova_ip" euca-run-instances "$test_image" -k mykey -t m1.tiny |tee \ + "$LOGDIR/nova-run-instance.status" +tries=0 +img_addr='' + +while ((tries++ < 30)); do + while read ltype l_id img_id img_addr1 img_addr2 img_status rest; do + [[ $img_id = $test_image ]] || continue + [[ $img_status =~ fail ]] && { + echo "Nova reports that $test_image failed to deploy" + cat "$LOGDIR/nova-running-instances.$tries.log" + exit 1 + } + [[ $img_id = $test_image && $img_status = running ]] || continue + img_addr=${img_addr1} + done < <(nova_do "$nova_ip" euca-describe-instances | \ + tee "$LOGDIR/nova-running-instances.$tries.log") + cat "$LOGDIR/nova-running-instances.$tries.log" + [[ $img_addr ]] && break + sleep 10 +done +if [[ ! $img_addr ]]; then + echo "$test_image failed to deploy after ${tries}0 seconds!" + exit 1 +fi +echo "Launched. Testing network connectivity..." +tries=0 +while ! nova_do "$nova_ip" ping -n -c 1 "$img_addr" >> "$LOGDIR/nova-ping-$img_addr.log"; do + if ((tries++ > 60)) ; then + cat "$LOGDIR/nova-ping-$img_addr.log" + echo "Could not ping $img_addr from $nova_ip" + exit 1 + fi +done +echo "Could ping deployed image." +tries=0 + +## The SSH test is known to not work right now. +# Leave it enabled, but do not fail the smoketest if it fails. +while ! nova_do "$nova_ip" ssh -o 'StrictHostKeyChecking no' -o 'UserKnownHostsFile /dev/null' -i mykey.priv ubuntu@"$img_addr" date >> "$LOGDIR/nova-ssh-$img_addr.log"; do + if ((tries++ > 60)) ; then + cat "$LOGDIR/nova-ssh-$img_addr.log" + echo "Could not ssh $img_addr from $nova_ip" + exit 0 + fi +done + +echo "Nova deploy passed." +exit 0 diff --git a/crowbar/test_framework/nova_test/README b/crowbar/test_framework/nova_test/README new file mode 100644 index 00000000000..8093ff71bc3 --- /dev/null +++ b/crowbar/test_framework/nova_test/README @@ -0,0 +1,4 @@ +Drop test hooks you want to run after all the cluster nodes are deployed. +They will get the IP of the admin node in the ADMIN_NODE_IP environment +variable, should have a numeric prefix and a suffix of .hook. +The hooks will be run in lexicographic sort order. \ No newline at end of file diff --git a/crowbar/test_framework/swift_test/00-swift-deploy.hook b/crowbar/test_framework/swift_test/00-swift-deploy.hook new file mode 100755 index 00000000000..ccfcb7e9989 --- /dev/null +++ b/crowbar/test_framework/swift_test/00-swift-deploy.hook @@ -0,0 +1,101 @@ +#!/bin/bash +# This does a test deploy of Swift +die () { local _res=$1; shift; echo "$@"; exit $_res; } + +[[ $DEBUG ]] && set -x +set -e + +crowbar swift proposal create cluster1 +crowbar swift proposal show cluster1 >"$LOGDIR/swift-proposal.json" +crowbar swift proposal commit cluster1 || { + echo "Swift proposal failed to commit" + exit 1 +} + +crowbar swift show cluster1 > "$LOGDIR/swift-cluster1.json" +read proxy < <(sed -n '/"swift-proxy-acct": \[/ { n; p }' \ + "$LOGDIR/swift-cluster1.json") +proxy="${proxy//\"/}." +addr_re='Address: ([0-9.]+)' +if ! [[ $(nslookup "$proxy" "$CROWBAR_IP") =~ $addr_re ]]; then + crowbar swift show cluster1 >"$LOGDIR/swift-failed.json" + die 1 "Could not find IP address of the swift proxy ($proxy)" +fi +proxy_ip="${BASH_REMATCH[1]}" +crowbar swift show cluster1 >"$LOGDIR/swift-deployed.json" +echo "Swift proxy at $proxy_ip. Prepping the Storage subsystem..." +while ! run_on "$proxy_ip" swauth-prep -K swauth -U .super_admin \ + -A 'https://127.0.0.1:8080/auth/'; do + echo "Unable to prep the authentication subsystem, rebooting proxy node." + echo "This is definitly a big in the way Swift is deployed." + run_on $proxy_ip reboot + sleep 120 +done + +echo "Creating an account..." +while ! run_on "$proxy_ip" swauth-add-account -K swauth -U .super_admin \ + -A https://127.0.0.1:8080/auth/ a_test; do + echo "Unable to create an account, rebooting proxy node." + echo "This is definitly a bug in the way Swift is being deployed." + run_on "$proxy_ip" reboot + sleep 120 +done + +echo "Creating a user..." +while ! run_on "$proxy_ip" swauth-add-user -K swauth -U .super_admin \ + -A https://127.0.0.1:8080/auth/ -a a_test test test; do + echo "Unable to create a user, rebooting proxy node." + echo "This is definitly a bug in the Swift deploy." + run_on "$proxy_ip" reboot + sleep 120 +done + +auth_hdr=$(mktemp "$LOGDIR/swift-auth-header-XXXXX.txt") || \ + die 1 "Could not make a temp location for the aithentication header." + +# get the auth information we will need +while read line; do + echo "$line" >>"$auth_hdr" + hdr=${line%%:*} + param=${line#*:} + case $hdr in + X-Storage-Url) storage_url=$param;; + X-Storage-Token) storage_token=$param;; + X-Auth-Token) auth_token=$param;; + esac +done < <(curl -k -D - -H -o /dev/null "X-Auth-User: a_test:test" \ + -H "X-Auth-Key: test" --connect-timeout 30 --max-time 120 \ + https://$proxy_ip:8080/auth/v1.0/) + +[[ $storage_url && $storage_token && $auth_token ]] || \ + die 1 "Could not get a necessary parameter from the Swift proxy!" + +# $1 = http method +# $2 = pathname for swift +# $3 = local path (optional) +do_swift() { + local curlargs=(-k -D - + -H "X-Auth-Token: $auth_token" + -H "X-Auth-User: a_test:test" + -X "$1" + "$storage_url/$2") + if [[ $3 && $1 = PUT ]]; then + curlargs+=(-T "$3") + elif [[ $3 && $1 = GET ]]; then + curlargs+=(-o "$3") + fi + curl "${curlargs[@]}" || return 1 +} + +echo "Testing Swift functionality..." +do_swift PUT testdir || die 1 "Could not create a directory in Swift" +testfile=$(mktemp "$LOGDIR/swift-testfile-XXXXX") +outfile=$(mktemp "$LOGDIR/swift-outfile-XXXXX") +dd if=/dev/urandom "of=$testfile" bs=64K count=1 +do_swift PUT testdir/testfile "$testfile" || \ + die 1 "Could not store random information into Swift" +do_swift GET testdir/testfile "$outfile" || \ + die 1 "Could not get our random data back from Swift" +echo "Swift deploy passed." +exit 0 + diff --git a/crowbar/test_framework/test_crowbar.sh b/crowbar/test_framework/test_crowbar.sh new file mode 100755 index 00000000000..c797244ae46 --- /dev/null +++ b/crowbar/test_framework/test_crowbar.sh @@ -0,0 +1,1115 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +# This script expects to be able to run certian commands as root. +# Either run it as a user who can sudo to root, or give the user +# you are running it as the following sudo rights: +# crowbar-tester ALL = NOPASSWD: /bin/mount, /bin/umount, /bin/ip, /usr/sbin/brctl, /home/crowbar-tester/test_framework/make-cgroups.sh + +# We expect to live in $HOME/test_framework. +# We use bash4 specific functionality (hash tables), and cgroups to +# make sure we clean up everything when we exit a test run. +# You will need a fairly revent Linux distro to run this test -- +# RHEL/CentOS 5 will not work without some significant rework. + +# always run in debugging mode, and log everything to a test log +exec 2>"$HOME/smoketest.log" +export DEBUG=true +export PS4='(${nodename:-none})${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): '; +export CROWBAR_KEY="crowbar:crowbar" +set -x + +# die horribly if any simple command fails. +set -e + +# Lockfiles to ensure that we don't accidentally end up +# stepping on our toes. + +# This lock is held whenever we are copying an openstack .ISO +# around to get it ready for testing. +UNTESTED_LOCK="$HOME/.untested.lck" + +# THis lock is held whenever we are running tests. It exists to +# prevent multiple instances of the smoketest from running at once. +TESTING_LOCK="$HOME/.testing.lck" + +# This lock is held whenever we are starting or killing KVM. +# We use it to ensure that only one virtual machine is being set +# up or torn down at any given time, and that our teardown +# function does not race with our launching function. +KVM_LOCK="$HOME/.kvm.lck" + +# This lock is held whenever we are performing a cleanup. +# We want to ensure that we never try to run two +# cleanups in parallel. +CLEANUP_LOCK="$HOME/.cleanup.lck" + +FRAMEWORKDIR="$HOME/test_framework" + +# We run all KVM instances in a screen session. +SCREENNAME="crowbar-test" + +# The number of computer nodes we will end up deploying. +# Each node has 2 gigs of memory, 2 cores, and a 6 gig disk +# image, so make sure your test machine can handle the load. +VIRT_NODES=(virt-1 virt-2 virt-3 virt-4) + +# Number of seconds we wait for compute nodes to go through a full deploy. +COMPUTE_DEPLOY_WAIT=2400 + +# IP address of the node that ends up as the admin node. +export CROWBAR_IP=192.168.124.10 + +# The names of the bridges we will create. Bridge names must be +# 15 characters or less due to kernel name constraints. +# The last part of the name will be used in tap interface name generation. +# Please keep it at 4 characters or less. +BRIDGES=(crowbar-pub crowbar-priv) + +# An array of physical interfaces and the bridges they should be bound to. +# We need to use real physical interfaces becasue Crowbar assumes +# it can create and destroy vlans as needed. +# Each entry in this array is if the form ifname,bridgename +# PHYSICAL_INTERFACES=(eth1,crowbar-pub) + +# An array of MAC addresses of the primary interfaces of the physical machines. +# We need to have this information beforehand so that we can send +# Wake On LAN packets to them. +PHYSICAL_MACS=() + +# Source a local config file. Settings in it will override the +# ones here. + +if [[ -f $HOME/.test-crowbar ]]; then + . "$HOME/.test-crowbar" +fi + +# A hash containing slave name -> hostname mappings. +declare -A SLAVES + +# Does exactly what it says. +# $1 = status to exit with +# $2 = error message to print. +die() { local _res=$1; shift; echo "$*"; exit $_res; } + +grep -q cgroup /proc/filesystems || die 1 "We must have cgroups to track our processes for proper cleanup!" + +update_status() { + # $1 = VM status to update. + # $2 = Status update + local current_date=$(date '+%F %T %z') + echo "$current_date: $1 - $2" + { + flock 66 + echo "$current_date: $2" >> "$testdir/$1.status" + } 66>"$testdir/.$1.status.lck" +} + +# make the bridges we will use for testing. +make_bridges() { + local pub_re='pub$' + local bridge + for bridge in "${BRIDGES[@]}"; do + sudo brctl show |grep -q "$bridge" || \ + sudo brctl addbr "$bridge" || \ + die -1 "Could not create $bridge bridge!" + sudo brctl setfd "$bridge" 1 || \ + die -3 "Could not set forwarding time on $bridge!" + sudo ip link set "$bridge" up || \ + die -2 "Could not set link on $bridge up!" + sudo brctl stp "$bridge" on + if [[ $bridge =~ $pub_re ]]; then + sudo ip addr add 192.168.124.1/24 dev "$bridge" + fi + done + # Bind the physical nics we want to use to the appropriate bridges. + for iface in "${PHYSICAL_INTERFACES[@]}"; do + local ifname=${iface%%,*} + local bridge=${iface##*,} + sudo ip link set "$ifname" up + sudo brctl addif "$bridge" "$ifname" + done +} + +# remove our bridges after we are done with them. +kill_bridges() { + # Ignore any simple errors in this function. + set +e + # Unbind any physical nics we have bound. + for iface in "${PHYSICAL_INTERFACES[@]}"; do + local ifname=${iface%%,*} + local bridge=${iface##*,} + sudo ip link set "$ifname" down + sudo brctl delif "$bridge" "$ifname" + done + # Tear down the bridges we created. + for bridge in "${BRIDGES[@]}"; do + sudo ip link set "$bridge" down + sudo brctl delbr "$bridge" + done +} + +get_cluster_logs() ( + set +e + echo "$(date '+%F %T %z'): Gathering $1 logs, please wait." + cd "$LOGDIR" + [[ -f $testdir/admin.pid ]] || return 1 + local curlargs=(-L -o "$1-$(date '+%Y%m%d-%H%M%S').tar" \ + --connect-timeout 120 --max-time 120) + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) || : + curl "${curlargs[@]}" "http://$CROWBAR_IP:3000/support/logs" + echo "$(date '+%F %T %z'): Done gathering $1 logs." +) + +# Tidy up after ourselves when we exit. +# This take care to make sure we don't have any stray +# child processes left behind (including VMs), tear down any +# network infrastructure we created, and create any success +# or failure logs we need to create. +declare -a cleanup_cmds taps +cleanup() { + # We ignore errors in this function. + set +e + { + # Make sure only one instance of cleanup is running + # at any given time. + flock 70 + # Once we have started running, make sure we are not invoked again. + trap 0 + killall check_ready + [[ $develop_mode = true ]] && pause + # Gather final logs if our admin node is up. + get_cluster_logs final + # Make sure our virtual machines have been torn down. + for pidfile in "$testdir/"*.pid; do + local vmname=${pidfile##*/} + vmname=${vmname%.pid} + kill_vm "$vmname" || : + done + # If there are any commands to run at cleanup, run them now. + for c in "${cleanup_cmds[@]}"; do + eval $c || : + done + # If our .iso is still mounted, umount it. + sudo /bin/umount -d "$LOOPDIR" &>/dev/null + # Tear down out network. + kill_virt_net &>/dev/null + # Kill our screen session + screen -S "$SCREENNAME" -X quit &>/dev/null + # Kill anything else we make have left behind. + for task in $(cat "$CGROUP_DIR/tasks"); do + [[ $task = $$ || $task = $SMOKETEST_PID ]] && continue + kill -9 $task + done + # Make sure we exit with the right status code. + [[ $final_status ]] || exit 1 + # Copy passed and failed logs to the right location. + [[ $test_results ]] && { + for result in "${test_results[@]}"; do + echo "$result" + [[ $result = *Failed ]] && final_status=Failed + done + } + echo "Deploy $final_status." + target="$HOME/tested/${testdir##*/}-$(date '+%Y%m%d-%H%M%S')-${final_status}" + rm -f "$testdir/"*.disk || : + mv "$testdir" "$target" + cp "$HOME/smoketest.log" "$target" + (cd "$HOME/tested"; tar czf "$target.tar.gz" "${target##*/}") + echo "Logs are available at $target.tar.gz." + echo "(on the Web at http://10.9.244.31:3389/crowbar-test/${target##*/}/)" + rm "$HOME/tested/"*.iso + mv "$ISO" "$HOME/tested" + if [[ $final_status != Passed ]]; then + ret=1 + else + ret=0 + fi + } 70>"$CLEANUP_LOCK" + exit $ret +} + +# Simple unique mac address generation. +# This will not work if you end up spinning up more than 65535 vm interfaces. +MACNR=1 +getmac() { + MACADDR=$(printf "52:54:%02x:12:34:%02x" $((MACNR/256)) $((MACNR%256))) + ((MACNR++)) + : +} + +# Make a tap interface, and attach it to the right bridge. +maketap() { + # $1 = preferred device name + # $2 = bridge to attach it to. + + # preemptively arrange to clean up. + sudo ip tuntap add dev "$1" mode tap || \ + die -1 "Could not create tap device $1" + sudo ip link set "$1" up || \ + die -2 "Could not bring link on tap device $1 up!" + sudo brctl addif "$2" "$1" || \ + die -3 "Could not add tap $1 to bridge $2!" +} + +# Remove a tap interface we created. +killtap() { + set +e + local res_re='(does not exist|Cannot find device)' + # $1 = device to kill + # $2 = bridge to detach it from + while ! [[ $(sudo ip link show "$1" 2>&1) =~ $res_re ]]; do + sudo brctl delif "$2" "$1" + sudo ip link set "$1" down + sudo ip tuntap del dev "$1" mode tap + done +} + +# Build up our local virtual net infrastructure. +make_virt_net() { + make_bridges + local node bridge + for node in admin ${VIRT_NODES[@]}; do + for bridge in "${BRIDGES[@]}"; do + local nic_name="${node}-${bridge##*-}" + maketap "$nic_name" "$bridge" + done + done +} + +# Tear down our local virtual net infrastructure. +kill_virt_net() { + set +e + local node bridge + for node in admin ${VIRT_NODES[@]}; do + for bridge in ${BRIDGES[@]}; do + local nic_name="${node}-${bridge##*-}" + killtap "$nic_name" "$bridge" + done + done + kill_bridges +} + +# Make MAC addresses for the network interfaces for a VM. +makenics() { + # $1 = base name for each nic. Must be 9 characters or less. + vm_nics=() + local node bridge + for bridge in ${BRIDGES[@]}; do + local nic_name="$1-${bridge##*-}" + getmac + if [[ $nic_name =~ virt-.-pub ]]; then + SLAVES["$1"]="d${MACADDR//:/-}.pod.cloud.openstack.org" + fi + vm_nics+=("$MACADDR,$nic_name") + done +} + +# find the newest file. Returns it as $NEWEST_FILE +find_newest_file() { + # $@ = files to discriminate against + local age newest_age f + unset NEWEST_FILE + for f in "$@"; do + [[ -f $f ]] || continue + age="$(date -r "$f" '+%s')" + if ((age > newest_age)); then + newest_age=$age + NEWEST_FILE=$f + fi + done + [[ $NEWEST_FILE ]] && return 0 || return 1 +} + +# grab the latest iso from untested and populate testing with it. +# This should be called with the testing lock held, and it will +# grab the untested lock. +populate_testing() { + flock -x 200 + test_isos=("$HOME/openstack"*.iso "$HOME/testing/openstack"*.iso \ + "$HOME/tested/openstack"*.iso) + find_newest_file "${test_isos[@]}" || return 1 # nothing to do. + # If we have a sumfile, hack it up to point at the "right" .iso + # and check to see if our .iso checks out. + for sumfile in "${NEWEST_FILE}".*sum; do + [[ -f $sumfile ]] || continue + location=${sumfile%/*} + sumtype=${sumfile##*.} + sed -i "s@iso_library@${location}@" "$sumfile" + if ! "$sumtype" -c --status "$sumfile" &>/dev/null; then + die 1 "${NEWEST_FILE} corrupt, does not match ${sumfile}." + fi + done + # Delete any stale .*sum files. + find "$HOME" -name '*.iso.*sum' -delete &>/dev/null || : + + for iso in "${test_isos[@]}"; do + # handling for .iso files already in testing + if [[ $iso =~ /testing/ ]]; then + isodir="${iso%.iso}" + # clean up any stale test directories that may be laying around. + # We always want to start from a clean slate. + [[ -d $isodir ]] && rm -rf "$isodir" || : + # if it is not the newest iso, remove it. + [[ -f $iso && $iso != $NEWEST_FILE ]] && rm "$iso" || : + elif [[ $iso != $NEWEST_FILE ]]; then + # not the newest file, and not already in testing. Nuke it. + [[ -f $iso ]] && rm "$iso" || : + else + # This is the newest file. + # Make sure we have the whole thing. + this_stat=$(stat "$NEWEST_FILE") + while [[ $last_stat != $this_stat ]]; do + sleep 1 + last_stat="$this_stat" + this_stat=$(stat "$NEWEST_FILE") + done + + NEWEST_FILE="$HOME/testing/${NEWEST_FILE##*/}" + mv "$iso" "$HOME/testing" + fi + done + echo "$NEWEST_FILE" +} 200>"$UNTESTED_LOCK" + +# Kill a running VM. +kill_vm() ( + flock 65 + # $1 = vmname + # $2 = state to assign, defaults to killed. + set +e + local killsig=TERM killtries=0 killstate="${2:-killed}" + # If there is no PID file, assume that the VM is already dead. + [[ -f $testdir/$1.pid ]] || { + > "$testdir/$1.$killstate" + flock -u 65 + return 0 + } + local pid=$(cat "$testdir/$1.pid") + # If there is no entry for the VM process in /proc/ assume the VM is dead. + # If there is, cd into that directory so that we can be sure + # we are killing the right process and so that we can tell when the process + # is dead -- when the process has finished exiting, the kernel will + # unlink /proc/$pid and everything in it. Even if the kernel manages + # to cycle through pidspace to create another process with the same pid + # between kill attempts, we will not see entry for that process in + # our $PWD, because it will be a different dentry and set of inodes that + # happen to have the same name. + cd "/proc/$pid" &>/dev/null || { + rm -f "$testdir/$1.pid" &>/dev/null + > "$testdir/$1.$killstate" + flock -u 65 + return 0 + } + # If the cmdline entry does not exist in /proc/$pid, the process is + # already dead but the kernel has not finished cleaning up. + [[ -f cmdline ]] || { + rm -f "$testdir/$1.pid" &>/dev/null + > "$testdir/$1.$killstate" + flock -u 65 + return 0 + } + # If the /proc/$pid/cmdline does not contain the name of our VM, + # the kernel has finished cleaning up our process and enough processes + # have been spawned that our PID has been reused. + grep -q "$1" cmdline || { + rm -f "$testdir/$1.pid" &>/dev/null + > "$testdir/$1.$killstate" + flock -u 65 + return 0 + } + # Loop trying to kill this VM. Escalate our violence and sleep longer + # the more tries we take. + while (( killtries++ < 10)); do + update_status $vmname "Killing $vm_gen (try $killtries, signal $killsig)" + kill "-$killsig" "$pid" + ((killtries < 5)) || killsig=KILL + sleep $killtries + # if /proc/$pid/cmdline (or any other file that normally exists there) + # is gone, the process is dead, and our work is done. + if [[ ! -f cmdline ]]; then + update_status $vmname "Killed with SIG${killsig}" + rm -f "$testdir/$vmname.pid" &>/dev/null + > "$testdir/$vmname.$killstate" + flock -u 65 + return 0 + fi + done + flock -u 65 + die -1 "Could not kill $vmname, something went horribly wrong." +) 65>"$KVM_LOCK" + +# Wait for a KVM instance to die naturally, for a timeout to expire, +# or for a daemonization condition to be reached. +wait_for_kvm() { + # $1 = name of KVM instance. + local vmname=$1 + shift + local pidfile="$testdir/$vmname.pid" + [[ -f $pidfile ]] || return 1 # no pidfile? Bad Things happened. + local kvmpid=$(cat "$pidfile") + while [[ $1 ]]; do + case $1 in + -timeout) local deadline=$(($(date +%s) + $2)) timeout=$2; shift;; + -daemonif) local daemonif=$2; shift;; + *) break;; + esac + shift + done + local lastres= thisres= + # Use the same /proc/$kvmpid trick we used in kill_vm to + # make sure we are watching the right process. + ( cd "/proc/$kvmpid" + # if /proc/$kvmpid/$cmdline does not contain the name of our + # VM, something went horrbly wrong. + [[ $(cat cmdline) =~ $vmname ]] || return 1 + while [[ -f cmdline ]]; do + # If there is a condition on which we should stop waiting for + # a VM, test to see if it is true. + if [[ $daemonif ]]; then + # We assign the output of $daemonif to a variable so that + # we don't spam up the test run transcript. + if thisres=$($daemonif); then + # If it is, stop watching this VM. + update_status "$vmname" "$thisres" + update_status "$vmname" \ + "Daemonizing node with $(($deadline - $(date +%s))) seconds left." + return 0 + elif [[ $thisres =~ problem && $vmname =~ admin ]]; then + update_status "$vmname" "$thisres" + update_status "$vmname" "Transition to problem state not allowed" + return 1 + elif [[ $thisres && $lastres != $thisres ]]; then + update_status "$vmname" "$thisres" + lastres="$thisres" + fi + fi + # If we were supposed to test for a deadline and we overran it, + # return with the appropriate status code. + if [[ $deadline ]] && (($(date +%s) > $deadline)); then + update_status "$vmname" "Node ran for more than $timeout seconds." + return 1 + fi + sleep 10 + done + # If we wanted to be daemonized but were not, game over man. + if [[ $daemonif ]]; then + update_status "$vmname" "Node failed to daemonize." + return 1 + else + # We appear to have exited normally. + update_status "$vmname" "Node exited." + return 0 + fi + ) +} + +# Hash that allows us to track the number of reboots a VM has had. +declare -A kvm_generations + +# Hack to pick the fastest disk caching mode. +# We use unsafe caching if we can on the vms because we will just +# rebuild the filesystems from scratch if anything goes wrong. +if kvm --help |grep -q 'cache.*unsafe'; then + drive_cache=unsafe +else + drive_cache=writeback +fi + +# Run a KVM session. +run_kvm() { + # $1 = name of KVM to run. Disk image and logfile names are + # derived from this. + # KVMs are always oneshot virtual machines -- this works around + # a few booting and rebooting bugs. + # $@ = after shifing, other args to be passed to kvm. + # In addition, we expect that the caller has set vm_nics appropriatly. + local waitargs=() reboot=false + while true; do + case $1 in + -daemon) waitargs+=('-daemonif' 'true');; + -timeout) waitargs+=("$1" "$2"); shift;; + -daemonif) waitargs+=("$1" "$2"); shift;; + -reboot) reboot=true;; + *) break;; + esac + shift + done + local vmname=$1 + shift + # Track the number of times that this VM has been started by the + # framework. + if [[ ! ${kvm_generations["$vmname"]} ]]; then + kvm_generations["$vmname"]=1 + else + kvm_generations["$vmname"]=$((${kvm_generations["$vmname"]} + 1)) + fi + local cpu_count=2 mem_size=2G + if [ "$vmname" == "admin" ] ; then + cpu_count=4 + mem_size=4G + fi + local vm_gen="$vmname.${kvm_generations[$vmname]}" + # create a new log directory for us. vm_logdir needs to be global + # because other things will need to read the logs to see what is happening. + vm_logdir="$testdir/logs/$vm_gen" + mkdir -p "$vm_logdir" + local pidfile="$testdir/$vmname.pid" + [[ -f $pidfile ]] && rm "$pidfile" || : + # Our initial array of args for kvm. + # We pass in our own ROM for PXE booting to work around some old + # PXE boot bugs in older versions of the E1000 etherboot roms. + local kvmargs=(-enable-kvm + -m $mem_size + -smp $cpu_count + -drive "file=$testdir/$vmname.disk,format=raw,cache=$drive_cache" + -serial "file:$vm_logdir/ttyS0.log" + -serial "file:$vm_logdir/ttyS1.log" + -option-rom "$FRAMEWORKDIR/8086100e.rom" + -pidfile "$pidfile" + -name "kvm-$vm_gen") + # Add appropriate nics based on the contents of vm_nics. + for line in "${vm_nics[@]}"; do + kvmargs+=(-net "nic,macaddr=${line%%,*},model=e1000") + kvmargs+=(-net "tap,ifname=${line##*,},script=no,downscript=no") + done + # Add additional disks if we have any. + for image in "$testdir/$vmname-"*".disk"; do + [[ -f $image ]] || continue + kvmargs+=(-drive "file=$image,format=qcow2,cache=$drive_cache") + done + + if [[ $reboot = false ]]; then + kvmargs+=(-no-reboot) + fi + { + # Make sure that we serialize KVM launches. + # Sometimes we get a boot failure if we try launching + # KVM instances in parallel. As long as we wait for + # the pidfile to be created, we should be OK -- kvm + # does not create the pidfile until it has finshed launching + # the VM. + flock 65 + # If we are running under X, then use a graphical display. + if [[ $DISPLAY ]]; then + kvmargs+=( -sdl -daemonize ) + kvm "${kvmargs[@]}" "$@" + else + # otherwise, launch ourselves under screen. + kvmargs+=( -curses ) + screen -S "$SCREENNAME" -X screen \ + -t "$vm_gen" kvm "${kvmargs[@]}" "$@" + screen -S "$SCREENNAME" -p "$vm_gen" -X log on + fi + # wait up to 10 seconds for a PID file + local s + for ((s=0; s<10; s++)); do + [[ -f $pidfile ]] && break + sleep 1 + done + flock -u 65 + } 65>"$KVM_LOCK" + # Now that we have launched the KVM instance, wait for it using the + # waitargs we were passed. + wait_for_kvm "$vmname" "${waitargs[@]}" || return 1 +} + +# This expects to be run with the testing lock held. +# It boots the admin node from the .iso in the testing directory, +# injecting the appropriate kernel parameters to make things +# work in the test environment. +run_admin_node() { + # mount our .iso file as a loopback device. Since the default + # setup tries to got straight to graphics mode and we want to + # test everything using a serial console, some Special Help + # is needed + + # First, set up our admin node. + local nodename="admin" + local vm_nics + makenics admin + + update_status admin "Mounting .iso" + sudo /bin/mount -o loop "$ISO" "$LOOPDIR" &>/dev/null || \ + die 1 "Could not loopback mount $ISO on $LOOPDIR." + cleanup_cmds+=("sudo /bin/umount -d '$LOOPDIR'") + + update_status admin "Hacking up kernel parameters" + # OK, now figure out what we need to grab by reading the + # isolinux.cfg file. + kernel_re='kernel([^/]+)(/.*)$' + append_re='append([^a-zA-Z/=?]+)(.*)$' + initrd_re='initrd=([^ ]+)' + console_re='console=([^ ]+)' + unset kernel kernel_params initrd + + while read line; do + [[ ! $kernel && ( $line =~ $kernel_re ) ]] && \ + kernel="$LOOPDIR/${BASH_REMATCH[2]}" || : + [[ ! $kernel_params && ( $line =~ $append_re ) ]] && \ + kernel_params=${BASH_REMATCH[2]} || : + [[ ! $initrd && $kernel_params && ( $kernel_params =~ $initrd_re ) ]] && { + kernel_params=${kernel_params/append=${BASH_REMATCH[1]}/} + initrd="$LOOPDIR/${BASH_REMATCH[1]}" + } || : + done < "$LOOPDIR/isolinux/isolinux.cfg" + + [[ $kernel && -f $kernel && $kernel_params && $initrd && -f $initrd ]] || \ + die -1 "Could not find our kernel!" + # create our admin disk image + update_status admin "Creating disk image" + screen -S "$SCREENNAME" -X screen -t Status "$HOME/test_framework/watch_Status.sh" + qemu-img create -f raw "$testdir/admin.disk" 6G &>/dev/null + + # makenics populates vm_nics with the appropriate information for + # run_kvm. This part cannot run in a subshell, because it relies + # on getmac being able to hand out unique mac addresses. + kernel_params+=" crowbar.hostname=admin.pod.cloud.openstack.org crowbar.url=http://192.168.124.10:8091/config crowbar.debug.logdest=/dev/ttyS0 crowbar.use_serial_console=true" + [[ $DISPLAY ]] || kernel_params+=" console=ttyS1,115200n81" + [[ -r $HOME/.ssh/id_rsa.pub ]] && kernel_params+=" crowbar.authkey=$(sed 's/ /\\040/g' <"$HOME/.ssh/id_rsa.pub")" + update_status admin "Performing install from ${ISO##*/}" + if ! run_kvm -timeout 1200 "$nodename" -cdrom "$ISO" -boot 'once=d' \ + -kernel "$kernel" -initrd "$initrd" \ + -append "$kernel_params"; then + update_status "$nodename" "Failed to install admin node after 1200 seconds." + update_status "$nodename" "Node failed to deploy." + return 1 + fi + + # Once this is finished, we no longer need the .iso image mounted. + sudo /bin/umount -d "$LOOPDIR" &>/dev/null + + # restart the admin node as a daemon, and wait for it to be ready to + # start installing compute nodes. + # for debugging, uncomment the next line. + update_status admin "Deploying admin node crowbar tasks" + if ! run_kvm -reboot -timeout 1800 \ + -daemonif "$FRAMEWORKDIR/check_ready admin.pod.cloud.openstack.org" \ + "$nodename"; then + update_status admin "Node failed to deploy." + return 1 + fi + # Once the KVM instance has launched, start watching the system log. + screen -S "$SCREENNAME" -X screen -t "Syslog Capture" \ + tail -f "$LOGDIR/admin.2/ttyS0.log" + # Grab the latest crowbar CLI code off the admin node. + ( cd "$HOME/testing/cli" + curlargs=(-L -o - --connect-timeout 120 --max-time 120) + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) || : + curl "${curlargs[@]}" "http://${CROWBAR_IP}:3000/support/get_cli" | \ + tar xzvf - + # make run_on + cat >"$HOME/testing/cli/run_on" <<"EOF" +#!/bin/bash +target="$1" +shift +[[ $1 ]] || exit 1 +ip_re='([0-9]{1,3}\.){3}[0-9]{1,3}' +addr_re='Address: ([0-9.]+)' +if ! [[ $target =~ $ip_re ]]; then + if [[ ! $(nslookup "$target" "$CROWBAR_IP") =~ $addr_re ]]; then + echo "Could not find IP address $target" >&2 + exit 1 + fi + target=${BASH_REMATCH[1]} +fi +ssh -q -o "UserKnownHostsFile /dev/null" -o "StrictHostKeyChecking no" \ + -o "PasswordAuthentication no" root@"$target" "$@" +EOF + # make a cheesy knife command + cat >"$HOME/testing/cli/knife" <<"EOF" +#!/bin/bash +run_on "$CROWBAR_IP" knife "$@" +EOF + chmod 755 "$HOME/testing/cli"/* + ) || : + run_on "$CROWBAR_IP" cp /root/.ssh/authorized_keys \ + /tftpboot/ubuntu_dvd/authorized_keys || : + # if there are any tests we should run on the admin node, do them now. + if run_hooks admin_deployed; then + update_status admin "Admin deploy tests passed" + else + update_status admin "Admin deploy tests failed." + local ret=1 + fi + + # If we were asked to pause, do so. + if [[ $pause_after_admin ]]; then + pause + fi + update_status admin "Node deployed." + get_cluster_logs admin-deployed + return ${ret:-0} +} + +# Create infrastructure for our slave nodes, but do not start them. +create_slaves() { + local nodename + # First, handle virtual machines. + for nodename in ${VIRT_NODES[@]}; do + local vm_nics=() + makenics "$nodename" + # Clear out our status, if one is lying around. + >"$testdir/$nodename.status" + >"$testdir/$nodename.reset" + qemu-img create -f raw "$testdir/$nodename.disk" 6G &>/dev/null + # Create a second and third image for Swift testing + qemu-img create -f qcow2 "$testdir/$nodename-01.disk" 1G &>/dev/null + qemu-img create -f qcow2 "$testdir/$nodename-02.disk" 1G &>/dev/null + # Run our VMs in the background, looping as we go. + ( + # Keep rebooting as long as the admin node is alive and + # the node has not been explicitly been killed by kill_vm. + count=0 + in_reset=false + while [[ -f $testdir/admin.pid && \ + ! -f $testdir/$nodename.killed ]]; do + # If we are in a reset state, spin until the .reset file is gone + # or we were asked to die. + + if [[ -f $testdir/$nodename.reset ]]; then + # If we were asked to reset the node, nuke it. + # For whatever reason if we just nuke the MBR d/i + # complains about the LVM VG even though it should not. + if [[ $in_reset != true ]]; then + update_status "$nodename" "Creating disk image" + rm -f "$testdir/$nodename"*.disk + qemu-img create -f raw "$testdir/$nodename.disk" 6G &>/dev/null + # Create a second and third image for Swift testing + qemu-img create -f qcow2 "$testdir/$nodename-01.disk" 1G &>/dev/null + qemu-img create -f qcow2 "$testdir/$nodename-02.disk" 1G &>/dev/null + in_reset=true + fi + + sleep 10 + continue + fi + in_reset=false + + # If we have a valid MBR on our primary disk, boot to it. + # This works around a bug in KVM where you cannot boot to a local + # disk if you are asked to while PXE booting. + if [[ $(hexdump -n 2 -s 0x1fe "$testdir/$nodename.disk") =~ \ + aa55 ]]; then + update_status "$nodename" "Booting node to disk ($((count++)))" + if run_kvm "$nodename"; then + kill_vm "$nodename" exited + else + update_status "$nodename" "Node failed to deploy." + kill_vm "$nodename" exited + fi + else + # Otherwise, PXE boot the machine. + update_status "$nodename" "PXE booting node ($((count++)))" + if run_kvm -timeout 1200 "$nodename" -boot 'order=n'; then + kill_vm "$nodename" exited + else + update_status "$nodename" "Node failed to deploy" + kill_vm "$nodename" timeout + fi + fi + done + update_status "$nodename" "Asked to be killed, giving up." + exit 0 + ) 2>"$testdir/$nodename.errlog" & + done + # Second, handle our physical machines. + local mac + for mac in "${PHYSICAL_MACS[@]}"; do + nodename="physical-$((i++))" + SLAVES["$nodename"]="d${mac//:/-}.pod.cloud.openstack.org" + > "$testdir/$nodename.status" + done +} + +# Deregister a node from Crowbar. +# The next time it boots Crowbar will restart the discovery cycle for it. +# $1 = the hostname of the node to deregister. +deregister_slave() { + # make Chef forget about the node. + crowbar machines delete "$1" +} + +# Reset nodes from Crowbar's standpoint -- that is, +# remove them from the Chef database and power them off. +# If the node is a VM, place it in the reset state. +reset_slaves() { + local slave hname + if [[ $develop_mode = true ]]; then + pause "Press any key to let slaves shut down." + fi + for slave in "${!SLAVES[@]}"; do + hname=${SLAVES["$slave"]} + update_status "$slave" "Asking $hname to shut down" + # If this is a virtual node, put it in reset state. + if [[ $slave =~ virt ]]; then + > "$testdir/$slave.reset" + fi + run_on "$hname" poweroff || : + done + echo "$(date '+%F %T %z'): Waiting for 2 minutes to allow nodes to power off" + sleep 120 + for slave in "${!SLAVES[@]}"; do + hname=${SLAVES["$slave"]} + update_status "$slave" "Forcing $hname to shut down" + if [[ $slave =~ virt ]]; then + kill_vm "$slave" reset || : + else + crowbar machines shutdown "$hname" + fi + deregister_slave "$hname" + done + sleep 30 # Make sure the delete finishes the chef-client run. +} + +# Wake up any slaves that are either powered off or in a reset state. +wakeup_slaves() { + local i=0 slave='' mac='' + for slave in "${!SLAVES[@]}"; do + > "$testdir/$slave.status" + echo "${SLAVES["$slave"]}" >"$testdir/$slave.hostname" + case $slave in + virt*) rm -f "$testdir/$slave.reset";; + physical*) mac="${SLAVES["$slave"]%%.*}" + mac=${mac//-/:} + wakeonlan -i 192.168.124.255 "${mac#d}" + sleep 1;; + esac + done +} + +# Deploy our slave nodes. +deploy_nodes() { + wakeup_slaves + # give things time to launch + sleep 60 + # Arrange to have the $testdir/$node.status file updated appropriatly + # when we think the node is deployed + local node + for node in "${!SLAVES[@]}"; do + ( + hname=${SLAVES["$node"]} + lastres='' res='' + while [[ -f $testdir/admin.pid && \ + ! -f $testdir/$node.killed ]]; do + if res=$("$FRAMEWORKDIR/check_ready" "$hname"); then + update_status "$node" "$res" + update_status "$node" "Node deployed." + exit 0 + elif [[ $res =~ [Dd]iscovered ]]; then + update_status "$node" "$res" + update_status "$node" "$(crowbar machines allocate "$hname")" + elif [[ $lastres != $res ]]; then + update_status "$node" "$res" + lastres="$res" + fi + sleep 10 + done + + ) & + done + # Wait for up to $COMPUTE_DEPLOY_WAIT seconds for our nodes to finish + # deploying. + local deadline=$(($(date +%s) + $COMPUTE_DEPLOY_WAIT)) + final_status=Waiting + while (($deadline > $(date +%s))) && \ + [[ $final_status = Waiting && -f $testdir/admin.pid ]]; do + final_status=Passed + for status in "$testdir/"*.status; do + [[ -f $status ]] || continue + if grep -q 'Node failed to deploy' "$status"; then + final_status=Failed + return 1 + elif ! grep -q 'Node deployed' "$status"; then + final_status=Waiting + fi + done + sleep 10 + done + [[ $final_status = Passed ]] || return 1 +} + +# run hooks. They will be sorted in lexicographic order, +# so naming them with numeric prefixes indicating the order +# they should run in is a Good Idea. +run_hooks() { + # $1 = hooks to run. Maps to $FRAMEWORKDIR/$1_hooks/. + # $2 = time to allow the tests to run for. + local deadline=$(($(date '+%s') + ${2:-300})) + ( sleep 1 + for hook in "$FRAMEWORKDIR/$1"/*.hook; do + if [[ -x $hook ]]; then + if "$hook"; then + continue + else + echo "Failed" >"$testdir/$1.test" + exit + fi + fi + done + echo "Passed" >"$testdir/$1.test" + ) & + local testpid=$! + sudo "$FRAMEWORKDIR/make_cgroups.sh" $testpid "crowbar-test/$1-test" + (cd /proc/$testpid + while [[ -f cmdline ]] && (($(date '+%s') <= $deadline)); do + sleep 10 + done + if [[ -f cmdline ]]; then + echo "Timed Out" >"$testdir/$1.test" + fi + ) + case $(cat "$testdir/$1.test") in + Passed) return 0;; + Failed) return 1;; + *) + for t in $(cat "$CGROUP_DIR/$1-test/tasks"); do + kill -9 "$t" + done + return 1;; + esac +} + +pause() { read -p "${1:-Press any key to continue:}" -n 1; } + +# This is the primary test-running function. +run_test() { + # If something already holds the testing lock, it is not safe to continue. + flock -n -x 100 || die -1 "Could not grab $TESTING_LOCK in run_test" + cd "$HOME/testing" + # Try to grab the latest openstack iso file to deploy from. + ISO=$(populate_testing) || \ + die -1 "Could not populate testing directory, nothing to test." + # $testdir is where we will store all our disk images and logfiles. + testdir="${ISO%.iso}" + for d in image logs; do mkdir -p "$testdir/$d"; done + LOOPDIR="$testdir/image" + export LOGDIR="$testdir/logs" + # Make sure we clean up after ourselves no matter how we exit. + trap cleanup 0 INT TERM QUIT + + # kill any already running screen sessions + screen_re="([0-9]+\.$SCREENNAME)" + while read line; do + [[ $line =~ $screen_re ]] && screen -S "${BASH_REMATCH[1]}" -X quit || : + done < <(screen -ls) + local tests_to_run=() test_results=() + # Process our commandline arguments. + while [[ $1 ]]; do + case $1 in + pause) pause_after_deploy=true;; + pause-after-admin) pause_after_admin=true;; + admin-only) admin_only=true;; + develop-mode) develop_mode=true;; + scratch) tests_to_run+=("$1") ;; + *) [[ -d $FRAMEWORKDIR/${1}_test ]] || \ + die 1 "Unknown test or option $1" + tests_to_run+=("$1");; + esac + shift + done + + # If no test was passed, run the fake test + [[ $tests_to_run ]] || tests_to_run+=("scratch") + + # make a screen session so that we can watch what we are doing if needed. + (cd "$testdir"; screen -d -m -S "$SCREENNAME" -t 'Initial Shell' /bin/bash) + # give a hard status line + screen -S "$SCREENNAME" -X hardstatus alwayslastline "%?%{yk}%-Lw%?%{wb}%n*%f %t%?(%u)%?%?%{yk}%+Lw%?" + # enable logging. + screen -S "$SCREENNAME" -X logfile "$LOGDIR/screenlog.%t" + # Build our local virtual network. + make_virt_net + final_status=Failed + # Launch our admin node, and then launch the compute nodes afterwards. + if run_admin_node 2>"$testdir/admin.errlog"; then + test_results+=("deploy_admin: Passed") + if [[ $admin_only ]]; then + final_status=Passed + cleanup + fi + create_slaves + for running_test in "${tests_to_run[@]}"; do + if ! deploy_nodes; then + test_results+=("$running_test: Failed") + echo "$(date '+%F %T %z'): $running_test deploy failed." + get_cluster_logs "$running_test-deploy-failed" + reset_slaves + continue + fi + echo "$(date '+%F %T %z'): $running_test deploy passed." + if ! run_hooks ${running_test}_test 900; then + echo "$(date '+%F %T %z'): $running_test tests failed." + test_results+=("$running_test: Failed") + get_cluster_logs "$running_test-tests-failed" + reset_slaves + continue + fi + echo "$(date '+%F %T %z'): $running_test tests passed." + get_cluster_logs "$running_test-tests-passed" + test_results+=("$running_test: Passed") + if [[ $pause_after_deploy || -f $testdir/pause ]]; then + pause + fi + reset_slaves + done + else + test_results=("Admin node: Failed") + fi + [[ "${test_results[*]}" =~ Failed ]] || final_status=Passed + cleanup +} 100>"$TESTING_LOCK" + +do_help() { + cat <, I will try to run +vms from the last failed run. + +Read my comments to learn how to tweak me. +EOF +} + +[[ -f $HOME/.ssh/known_hosts ]] && rm -f "$HOME/.ssh/known_hosts" || : +[[ -f $HOME/.ssh/id_rsa.pub ]] || { mkdir -p "$HOME/.ssh"; ssh-keygen -q -f "$HOME/.ssh/id_rsa" -t rsa -N '' ; } + +[[ -f $HOME/testing/cli ]] || mkdir -p "$HOME/testing/cli" +export PATH="$HOME/testing/cli:$PATH" + +export SMOKETEST_PID=$$ +CGROUP_DIR=$(sudo "$FRAMEWORKDIR/make_cgroups.sh" \ + $SMOKETEST_PID crowbar-test) || \ + die 1 "Could not mount cgroup filesystem!" + + +case $1 in + run-test) shift; run_test "$@" ;; + build-and-test) shift; + which build_crowbar.sh &>/dev/null || \ + die 1 "Cannot find build_crowbar.sh -- have you added the crowbar checkout to \$PATH?" + echo "$(date '+%F %T %z'): Building Crowbar from source." + build_crowbar.sh + echo "$(date '+%F %T %z'): Finshed building Crowbar." + run_test "$@";; + cleanup) shift; cleanup;; + '') run_test nova swift "$@";; + *) do_help;; +esac diff --git a/crowbar/test_framework/watch_Status.sh b/crowbar/test_framework/watch_Status.sh new file mode 100755 index 00000000000..61029bd0b7e --- /dev/null +++ b/crowbar/test_framework/watch_Status.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright 2011, Dell +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: VictorLowther +# + +while true; do + clear + for f in "$HOME/testing/"*/*.status; do + [[ -f $f ]] || exit 0 + echo "$f:" + cat "$f" + echo + done + sleep 5 +done \ No newline at end of file diff --git a/crowbar/ubuntu-10.10-extra/NOTES b/crowbar/ubuntu-10.10-extra/NOTES new file mode 100644 index 00000000000..e7acd1fe58c --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/NOTES @@ -0,0 +1,42 @@ + +To get through dell firewalls, you need to set the following environment variables (change to the appropriate thing): +export http_proxy='http://Americas\Gregory_Althaus:@proxy.us.dell.com:80' +export HTTP_PROXY='http://Americas\Gregory_Althaus:@proxy.us.dell.com:80' +export https_proxy='http://Americas\Gregory_Althaus:@proxy.us.dell.com:80' +export HTTPS_PROXY='http://Americas\Gregory_Althaus:@proxy.us.dell.com:80' + +To get canonical based debs: +1. Edit sources.list in the /etc/apt directory + a. Change all the #deb to deb + b. Comment out the local repos +2. Run: apt-get update +3. Run: rm /var/cache/apt/archives/*.deb +4. Run: apt-get install +5. Run: cp /var/cache/apt/archives/*.deb /tftpboot/ubuntu_dvd/extra/debs + +To get swift debs: +1. Add the swift repository + a. For latest stable: add-apt-repository ppa:swift-core/ppa + b. For latest dev: add-apt-repository ppa:swift-core/trunk +2. Run: apt-get update +3. Run: rm /var/cache/apt/archives/*.deb +4. Run: apt-get install python-swift +5. Run: apt-get install swift swift-proxy swift-account swift-object swift-container +6. Run: rm /tftpboot/ubuntu_dvd/extra/debs/openstack/swift/* +7. Run: cp /var/cache/apt/archives/*.deb /tftpboot/ubuntu_dvd/extra/debs/openstack/swift + +To get nova debs: +1. Add the nova repository + a. For latest stable: add-apt-repository ppa:nova-core/ppa + b. For latest dev: add-apt-repository ppa:nova-core/trunk +2. Run: apt-get update +3. Run: rm /var/cache/apt/archives/*.deb +4. Run: apt-get install python-nova +5. Run: apt-get install nova-common nova-api nova-objectstore nova-volume nova-compute nova-scheduler nova-doc nova-network nova-ajax-console-proxy +6. Run: rm /tftpboot/ubuntu_dvd/extra/debs/openstack/nova/* +7. Run: cp /var/cache/apt/archives/*.deb /tftpboot/ubuntu_dvd/extra/debs/openstack/nova + +To build a repos from this directory: +1. apt-get install build-essential +2. run: dpkg-scanpackages ./ /dev/null | gzip > Packages.gz + diff --git a/crowbar/ubuntu-10.10-extra/Packages.gz b/crowbar/ubuntu-10.10-extra/Packages.gz new file mode 100644 index 00000000000..414de96ad53 Binary files /dev/null and b/crowbar/ubuntu-10.10-extra/Packages.gz differ diff --git a/crowbar/ubuntu-10.10-extra/data_item.rb b/crowbar/ubuntu-10.10-extra/data_item.rb new file mode 100644 index 00000000000..fa24349ed0a --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/data_item.rb @@ -0,0 +1,108 @@ +# +# Author:: Adam Jacob () +# Author:: Christopher Brown () +# Author:: Nuo Yan () +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/data_bag' +require 'chef/data_bag_item' + +class DataItem < Application + + provides :json + + before :populate_data_bag + before :authenticate_every + before :is_admin, :only => [ :create, :update, :destroy ] + + def populate_data_bag + begin + @data_bag = Chef::DataBag.cdb_load(params[:data_bag_id]) + rescue Chef::Exceptions::CouchDBNotFound => e + raise NotFound, "Cannot load data bag #{params[:data_bag_id]}" + end + end + + def show + begin + @data_bag_item = Chef::DataBagItem.cdb_load(params[:data_bag_id], params[:id]) + rescue Chef::Exceptions::CouchDBNotFound => e + raise NotFound, "Cannot load data bag #{params[:data_bag_id]} item #{params[:id]}" + end + display @data_bag_item + end + + def create + raw_data = nil + if params.has_key?("inflated_object") + raw_data = params["inflated_object"].raw_data + else + raw_data = params + raw_data.delete(:action) + raw_data.delete(:controller) + raw_data.delete(:data_bag_id) + end + @data_bag_item = nil + begin + @data_bag_item = Chef::DataBagItem.cdb_load(@data_bag.name, params[:id]) + rescue Chef::Exceptions::CouchDBNotFound + @data_bag_item = Chef::DataBagItem.new + @data_bag_item.data_bag(@data_bag.name) + else + raise Conflict, "Databag Item #{params[:id]} already exists" if @data_bag_item + end + @data_bag_item.raw_data = raw_data + @data_bag_item.cdb_save + display @data_bag_item + end + + def update + raw_data = nil + if params.has_key?("inflated_object") + raw_data = params["inflated_object"].raw_data + else + raw_data = params + raw_data.delete(:action) + raw_data.delete(:controller) + raw_data.delete(:data_bag_id) + end + + begin + @data_bag_item = Chef::DataBagItem.cdb_load(@data_bag.name, params[:id]) + rescue Chef::Exceptions::CouchDBNotFound => e + raise NotFound, "Cannot load Databag Item #{params[:id]}" + end + + @data_bag_item.raw_data = raw_data + @data_bag_item.cdb_save + display @data_bag_item + + end + + + def destroy + begin + @data_bag_item = Chef::DataBagItem.cdb_load(params[:data_bag_id], params[:id]) + rescue Chef::Exceptions::CouchDBNotFound => e + raise NotFound, "Cannot load data bag #{params[:data_bag_id]} item #{params[:id]}" + end + @data_bag_item.cdb_destroy + @data_bag_item.couchdb_rev = nil + display @data_bag_item + end + +end diff --git a/crowbar/ubuntu-10.10-extra/debsel.conf b/crowbar/ubuntu-10.10-extra/debsel.conf new file mode 100644 index 00000000000..65ae15e4921 --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/debsel.conf @@ -0,0 +1,12 @@ +# New password for the 'admin' user in the Chef Server WebUI: +chef-server-webui chef-server-webui/admin_password password password +# New password for the 'chef' AMQP user in the RabbitMQ vhost "/chef": +chef-solr chef-solr/amqp_password password password +chef chef/chef_server_url string http://__HOSTNAME__:4000 + +# Do you accept the DLJ license terms? +sun-java6-bin shared/accepted-sun-dlj-v1-1 boolean true +sun-java6-jre shared/accepted-sun-dlj-v1-1 boolean true +sun-java6-jre sun-java6-jre/stopthread boolean true +sun-java6-jre sun-java6-jre/jcepolicy note + diff --git a/crowbar/ubuntu-10.10-extra/h2n.tar.gz b/crowbar/ubuntu-10.10-extra/h2n.tar.gz new file mode 100644 index 00000000000..f013f0c1df3 Binary files /dev/null and b/crowbar/ubuntu-10.10-extra/h2n.tar.gz differ diff --git a/crowbar/ubuntu-10.10-extra/index.html b/crowbar/ubuntu-10.10-extra/index.html new file mode 100644 index 00000000000..91e47203765 --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/index.html @@ -0,0 +1,12 @@ + + Welcome to Crowbar + + Here are some handy links: + + + diff --git a/crowbar/ubuntu-10.10-extra/install b/crowbar/ubuntu-10.10-extra/install new file mode 100755 index 00000000000..df29fe8151a --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/install @@ -0,0 +1,6 @@ +#!/bin/bash + +cd /tftpboot/ubuntu_dvd/extra +script -c "./install-chef.sh $1" /var/log/install.log +cd - + diff --git a/crowbar/ubuntu-10.10-extra/install-chef.sh b/crowbar/ubuntu-10.10-extra/install-chef.sh new file mode 100755 index 00000000000..a75b1aaa67a --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/install-chef.sh @@ -0,0 +1,268 @@ +#!/bin/bash + +export FQDN="$1" +export PATH="/opt/dell/bin:$PATH" +die() { local _r=$1; shift; echo "$(date '+%F %T %z'): $@"; exit $_r; } + +# mac address and IP address matching routines +mac_match_re='link/ether ([0-9a-fA-F:]+)' +ip_match_re='inet ([0-9.]+)' +get_ip_and_mac() { + local ip_line=$(ip addr show $1) + [[ $ip_line =~ $mac_match_re ]] && MAC=${BASH_REMATCH[1]} || unset MAC + [[ $ip_line =~ $ip_match_re ]] && IP=${BASH_REMATCH[1]} || unset IP +} + +crowbar_up= + +# Run a command and log its output. +log_to() { + # $1 = install log to log to + # $@ = rest of args + local __logname="$1" _ret=0 + local __log="/var/log/install-$1" + local __timestamp="$(date '+%F %T %z')" + local log_skip_re='^gem|knife$' + shift + printf "\n%s\n" "$__timestamp: Running $*" | \ + tee -a "$__log.err" >> "$__log.log" + "$@" 2>> "$__log.err" >>"$__log.log" || { + _ret=$? + if ! [[ $__logname =~ $log_skip_re ]]; then + echo "$__timestamp: $* failed." + echo "See $__log.log and $__log.err for more information." + fi + } + printf "\n$s\n--------\n" "$(date '+%F %T %z'): Done $*" | \ + tee -a "$__log.err" >> "$__log.log" + return $_ret +} + +chef_or_die() { + log_to chef blocking_chef_client.sh && return + if [[ $crowbar_up && $FQDN ]]; then + crowbar crowbar transition "$FQDN" problem + fi + # If we were left without an IP address, rectify that. + ip link set eth0 up + ip addr add 192.168.124.10/24 dev eth0 + die 1 "$@" +} + +# Keep trying to start a service in a loop. +# $1 = service to restart +# $2 = status messae to print. +restart_svc_loop() { + while service "$1" status | grep -qi fail + do + echo "$(date '+%F %T %z'): $2..." + log_to svc service "$1" start + sleep 1 + done +} + + +# Make sure there is something of a domain name +DOMAINNAME=${FQDN#*.} +[[ $DOMAINNAME = $FQDN || $DOMAINNAME = ${DOMAINNAME#*.} ]] && \ + die -1 "Please specify an FQDN for the admin name" + +# setup hostname from config file +echo "$(date '+%F %T %z'): Setting Hostname..." +update_hostname.sh $FQDN + +# Set up our eth0 IP address way in advance. +# Deploying Crowbar should also do this for us, but sometimes it does not. +# When it does not, things get hard to debug pretty quick. +ip link set eth0 up +ip addr add 192.168.124.10/24 dev eth0 + +# Replace the domainname in the default template +DOMAINNAME=$(dnsdomainname) +sed -i "s/pod.cloud.openstack.org/$DOMAINNAME/g" /opt/dell/chef/data_bags/crowbar/bc-template-dns.json + +# once our hostname is correct, bounce rsyslog to let it know. +log_to svc service rsyslog restart + +# +# Install the base deb packages +# +echo "$(date '+%F %T %z'): Installing Chef Server..." +log_to apt apt-get update +log_to apt sed -i "s/__HOSTNAME__/$FQDN/g" ./debsel.conf +log_to apt /usr/bin/debconf-set-selections ./debsel.conf +log_to apt apt-get -y install chef chef-server chef-server-webui \ + kwalify libcurl4-gnutls-dev build-essential ruby-dev + +echo "$(date '+%F %T %z'): Installing Gems..." +for gem in gems/*.gem; do + [[ -f $gem ]] || continue + gemname=${gem##*/} + gemname=${gemname%-*.gem} + log_to gem gem install "$gemname" --local "$gem" --no-ri --no-rdoc +done + +echo "$(date '+%F %T %z'): Building Keys..." +# Generate root's SSH pubkey +if [ ! -e /root/.ssh/id_rsa ] ; then + log_to keys ssh-keygen -t rsa -f /root/.ssh/id_rsa -N "" +fi +# add our own key to authorized_keys +cat /root/.ssh/id_rsa.pub >>/root/.ssh/authorized_keys +# and trick Chef into pushing it out to everyone. +cp /root/.ssh/authorized_keys \ + /opt/dell/chef/cookbooks/ubuntu-install/files/default/authorized_keys + +# generate the machine install username and password +if [[ ! -e /etc/crowbar.install.key ]]; then + dd if=/dev/urandom bs=65536 count=1 2>/dev/null |sha512sum - 2>/dev/null | \ + (read key rest; echo "machine-install:$key" >/etc/crowbar.install.key) + export CROWBAR_KEY=$(cat /etc/crowbar.install.key) + printf "${CROWBAR_KEY%%:*}:Crowbar:${CROWBAR_KEY##*:}" |md5sum - | ( + read key rest + printf "\n${CROWBAR_KEY%%:*}:Crowbar:$key\n" >> \ + /opt/dell/openstack_manager/htdigest) +else + export CROWBAR_KEY=$(cat /etc/crowbar.install.key) +fi +# Crowbar will hack up the pxeboot files appropriatly. + +# Set Version in Crowbar UI +VERSION=$(cat /opt/.dell-install/Version) +sed -i "s/CROWBAR_VERSION = .*/CROWBAR_VERSION = \"${VERSION:=Dev}\"/" \ + /opt/dell/openstack_manager/config/environments/production.rb + +# HACK AROUND CHEF-2005 +cp data_item.rb /usr/share/chef-server-api/app/controllers +log_to svc /etc/init.d/chef-server restart +# HACK AROUND CHEF-2005 + +restart_svc_loop chef-solr "Restarting chef-solr - spot one" + +chef_or_die "Initial chef run failed" +echo "$(date '+%F %T %z'): Validating data bags..." +log_to validation validate_bags.rb /opt/dell/chef/data_bags || \ + die -5 "Crowbar configuration has errors. Please fix and rerun install." + +# Run knife in a loop until it doesn't segfault. +knifeloop() { + local RC=0 _v=error + while { log_to knife knife "$@" -l "$_v" -u chef-webui \ + -k /etc/chef/webui.pem + RC=$? + _v=debug + (($RC == 139)); }; do + : + done +} + +cd /opt/dell/chef/cookbooks +echo "$(date '+%F %T %z'): Uploading cookbooks..." +knifeloop cookbook upload -a -o ./ + +cd ../roles +echo "$(date '+%F %T %z'): Uploading roles..." +for role in *.rb *.json +do + knifeloop role from file "$role" +done + +restart_svc_loop chef-solr "Restarting chef-solr - spot two" + +cd ../data_bags +echo "$(date '+%F %T %z'): Uploading data bags..." +for bag in * +do + [[ -d $bag ]] || continue + knifeloop data bag create "$bag" + for item in "$bag"/*.json + do + knifeloop data bag from file "$bag" "$item" + done +done + +echo "$(date '+%F %T %z'): Update run list..." +knifeloop node run_list add "$FQDN" role[crowbar] +knifeloop node run_list add "$FQDN" role[deployer-client] + +log_to svc service chef-client stop + +restart_svc_loop chef-solr "Restarting chef-solr - spot three" + +echo "$(date '+%F %T %z'): Bringing up Crowbar..." +# Run chef-client to bring-up crowbar server +chef_or_die "Failed to bring up Crowbar" +# Make sure looper_chef_client is a NOOP until we are finished deploying +touch /tmp/deploying + +# have chef_or_die change our status to problem if we fail +crowbar_up=true + +# Add configured crowbar proposal +if [ "$(crowbar crowbar proposal list)" != "default" ] ; then + proposal_opts=() + if [[ -e /tftpboot/ubuntu_dvd/extra/config/crowbar.json ]]; then + proposal_opts+=(--file /tftpboot/ubuntu_dvd/extra/config/crowbar.json) + fi + proposal_opts+=(proposal create default) + + # Sometimes proposal creation fails if Chef and Crowbar are not quite + # fully prepared -- this can happen due to solr not having everything + # fully indexed yet. So we don't want to just fail immediatly if + # we fail to create a proposal -- instead, we will kick Chef, sleep a bit, + # and try again up to 5 times before bailing out. + for ((x=1; x<6; x++)); do + crowbar crowbar "${proposal_opts[@]}" && { proposal_created=true; break; } + echo "Proposal create failed, pass $x. Will kick Chef and try again." + chef_or_die "Kicking proposal bits" + sleep 1 + done + if [[ ! $proposal_created ]]; then + die 1 "Could not create default proposal" + fi +fi +crowbar crowbar proposal show default >/var/log/default-proposal.json +crowbar crowbar proposal commit default || \ + die 1 "Could not commit default proposal!" +crowbar crowbar show default >/var/log/default.json +chef_or_die "Chef run after default proposal commit failed!" + +# transition though all the states to ready. Make sure that +# Chef has completly finished with transition before proceeding +# to the next. + +for state in "discovering" "discovered" "hardware-installing" \ + "hardware-installed" "installing" "installed" "readying" "ready" +do + while [[ -f "/tmp/chef-client.lock" ]]; do sleep 1; done + printf "$state: " + crowbar crowbar transition "$FQDN" "$state" || \ + die 1 "Transition to $state failed!" + chef_or_die "Chef run for $state transition failed!" +done + +# OK, let looper_chef_client run normally now. +rm /tmp/deploying + +# Spit out a warning message if we managed to not get an IP address +# on eth0 +get_ip_and_mac eth0 +[[ $IP && $MAC ]] || { + echo "$(date '+%F %T %z'): eth0 not configured, but should have been." + echo "Things will probably end badly." + echo "Going ahead and configuring eth0 with 192.168.124.10." + ip link set eth0 up + ip addr add 192.168.124.10/24 dev eth0 +} + +restart_svc_loop chef-client "Restarting chef-client - spot four" + +log_to apt apt-get -q --force-yes -y upgrade + +# transform our friendlier Crowbar default home page. +cd /tftpboot/ubuntu_dvd/extra +[[ $IP ]] && sed "s@localhost@$IP@g" < index.html >/var/www/index.html + +# Run tests -- currently the host will run this. +#/opt/dell/bin/barclamp_test.rb -t || \ +# die -6 "Crowbar validation has errors! Please check the logs and correct." diff --git a/crowbar/ubuntu-10.10-extra/packages/base.list b/crowbar/ubuntu-10.10-extra/packages/base.list new file mode 100644 index 00000000000..be2b86b4c4c --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/packages/base.list @@ -0,0 +1,98 @@ +# List of packages that we need to prepopulate the openstack .ISO with. +# repository = a debian repostiroy definition that will be added +# to sources.list. +# ppas A list of PPAs to grab packages from. +# debs = deb pacakges to prefetch +# gems = gems to prefetch + +# required Ubuntu repositories +repository deb http://us.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse +repository deb-src http://us.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse +repository deb http://us.archive.ubuntu.com/ubuntu/ maverick-updates main restricted universe multiverse +repository deb-src http://us.archive.ubuntu.com/ubuntu/ maverick-updates main restricted universe multiverse + +repository deb http://archive.canonical.com/ubuntu maverick partner +repository deb-src http://archive.canonical.com/ubuntu maverick partner + +repository deb http://extras.ubuntu.com/ubuntu maverick main +repository deb-src http://extras.ubuntu.com/ubuntu maverick main + +repository deb http://security.ubuntu.com/ubuntu maverick-security main restricted universe multiverse +repository deb-src http://security.ubuntu.com/ubuntu maverick-security main restricted universe multiverse + +# required Chef repository +repository deb http://apt.opscode.com/ maverick main + +# base Chef packages +debs chef chef-server chef-server-webui kwalify libcurl4-gnutls-dev build-essential ruby-dev rubygems1.8 + +# base gems we will need +gems activesupport-3.0.8 haml-3.1.1 i18n-0.6.0 json-1.5.2 net-http-digest_auth-1.1.1 patron-0.4.11 sass-3.1.2 simple-navigation-3.3.0 syslogger-1.2.4 passenger net-ssh-multi + +# additional debs we will need +# apache stuff +debs apache2 libapache2-mod-wsgi libapache2-mod-php5 apache2-prefork-dev libopkele-dev libopkele3 libapache2-mod-python libapache2-svn libapache2-mod-fcgid libapache2-mod-passenger + +# sun java +debs sun-java6-jdk sun-java6-jre + +# apt-cacher stuff +debs apt-cacher + +# mysql stuff +debs libmysqlclient-dev mysql-client libmysql-ruby mysql-server + +# openssh stuff +debs openssh-client openssh-server + +# openldap stuff +debs ldap-utils db4.8-util slapd libnss-ldap libpam-ldap + +# runit stuff +debs runit + +# sudo stuff +debs sudo + +# rabbitmq stuff +debs rabbitmq-server + +# minicom stuff +debs minicom + +# rails stuff +debs rails + +# build essential stuff +debs build-essential binutils-doc autoconf flex bison + +# ruby stuff +debs ruby1.8 ruby1.8-dev libshadow-ruby1.8 + +# ncsd stuff +debs nscd + +# nagios stuff +debs nagios-nrpe-server nagios-plugins nagios-plugins-basic nagios-plugins-standard libjson-perl libmath-calc-units-perl libnagios-plugin-perl libnagios-object-perl libparams-validate-perl nagios3 nagios-nrpe-plugin nagios-images + +# glance stuff +debs glance python-glance + +# bind9 stuff +debs bind9 bind9utils + +# ganglia stuff +debs ganglia-monitor gmetad ganglia-webfrontend + + +# ntp stuff +debs ntp + +# rsyslog stuff +debs rsyslog + +# ipmi stuff +debs ipmitool + +# network stuff +debs bridge-utils vlan ifenslave-2.6 dhcp3-server tftpd-hpa nfs-common portmap nfs-kernel-server diff --git a/crowbar/ubuntu-10.10-extra/packages/nova.list b/crowbar/ubuntu-10.10-extra/packages/nova.list new file mode 100644 index 00000000000..0a0560fcbc7 --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/packages/nova.list @@ -0,0 +1,7 @@ +ppas openstack-release/2011.2 + +# nova stuff +debs euca2ools unzip iscsitarget nova-volume libvirt-bin nova-compute kvm nova-network nova-common python-mysqldb dnsmasq-base python-nova nova-api nova-objectstore nova-volume nova-scheduler nova-doc nova-ajax-console-proxy python-eventlet + +# nova-dashboard stuff +debs python-pip python-virtualenv bzr diff --git a/crowbar/ubuntu-10.10-extra/packages/swift.list b/crowbar/ubuntu-10.10-extra/packages/swift.list new file mode 100644 index 00000000000..9513debff88 --- /dev/null +++ b/crowbar/ubuntu-10.10-extra/packages/swift.list @@ -0,0 +1,4 @@ +ppas openstack-release/2011.2 + +# swift stuff +debs curl python-software-properties swift swift-container swift-object sqlite swift-account swift-account memcached swift-proxy xfsprogs libmemcache-dev python-swift diff --git a/crowbar/updates/control.sh b/crowbar/updates/control.sh new file mode 100755 index 00000000000..f36084a25a0 --- /dev/null +++ b/crowbar/updates/control.sh @@ -0,0 +1,115 @@ +#!/bin/bash + +# We get the following variables from start-up.sh +# MAC BOOTDEV ADMIN_IP DOMAIN HOSTNAME HOSTNAME_MAC MYIP + +MYINDEX=${MYIP##*.} +STATE=`grep dhcp-client-state /var/lib/dhclient/dhclient*.leases | uniq | cut -d" " -f5 | cut -d";" -f1` +DEBUG=`grep dhcp-client-debug /var/lib/dhclient/dhclient*.leases | uniq | cut -d" " -f5 | cut -d";" -f1` +export BMC_ADDRESS="" +export BMC_NETMASK="" +export BMC_ROUTER="" + +# Make sure date is up-to-date +while ! /usr/sbin/ntpdate $ADMIN_IP +do + echo "Waiting for NTP server" + sleep 1 +done + +# HACK fix for chef-client +cd /root +gem install --local rest-client +cd - + +# Add full code set +if [ -e /updates/full_data.sh ] ; then + /updates/full_data.sh +fi + +# get validation cert +curl -L -o /etc/chef/validation.pem \ + --connect-timeout 60 -s \ + "http://$ADMIN_IP:8091/ubuntu_dvd/validation.pem" + +parse_node_data() { + for s in $(/updates/parse_node_data -a name -a crowbar.network.bmc.netmask -a crowbar.network.bmc.address -a crowbar.network.bmc.router -a crowbar.allocated $1) ; do + VAL=${s#*=} + case ${s%%=*} in + name) export HOSTNAME=$VAL;; + crowbar.allocated) export NODE_STATE=$VAL;; + crowbar.network.bmc.router) export BMC_ROUTER=$VAL;; + crowbar.network.bmc.address) export BMC_ADDRESS=$VAL;; + crowbar.network.bmc.netmask) export BMC_NETMASK=$VAL;; + esac + done + + echo BMC_ROUTER=${BMC_ROUTER} + echo BMC_ADDRESS=${BMC_ADDRESS} + echo BMC_NETMASK=${BMC_NETMASK} + echo HOSTNAME=${HOSTNAME} + echo NODE_STATE=${NODE_STATE} +} + + +post_state() { + local curlargs=(-o "/tmp/node_data.$$" --connect-timeout 60 -s \ + -L -X POST --data-binary "{ \"name\": \"$1\", \"state\": \"$2\" }" \ + -H "Accept: application/json" -H "Content-Type: application/json") + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) + curl "${curlargs[@]}" "http://$ADMIN_IP:3000/crowbar/crowbar/1.0/transition/default" + parse_node_data /tmp/node_data.$$ + rm /tmp/node_data.$$ +} + +get_state() { + local curlargs=(-o "/tmp/node_data.$$" --connect-timeout 60 -s \ + -L -H "Accept: application/json" -H "Content-Type: application/json") + [[ $CROWBAR_KEY ]] && curlargs+=(-u "$CROWBAR_KEY" --digest) + curl "${curlargs[@]}" "http://$ADMIN_IP:3000/crowbar/machines/1.0/show?name=$HOSTNAME" + parse_node_data /tmp/node_data.$$ + rm /tmp/node_data.$$ +} + +maybe_reboot () { [[ $DEBUG != 1 ]] && reboot; } + +run_chef () { + chef-client -S http://$ADMIN_IP:4000/ -N $1 +} + +case $STATE in + 0) echo "Discovering with: $HOSTNAME_MAC" + post_state $HOSTNAME_MAC discovering + run_chef $HOSTNAME_MAC + post_state $HOSTNAME_MAC discovered + + while [ "$NODE_STATE" != "true" ] ; do + sleep 15 + get_state + done + + echo "Hardware installing with: $HOSTNAME" + rm -f /etc/chef/client.pem + post_state $HOSTNAME hardware-installing + run_chef $HOSTNAME + post_state $HOSTNAME hardware-installed + sleep 30 # Allow settle time + maybe_reboot;; + 1) while [ "$NODE_STATE" != "true" ] ; do + sleep 15 + get_state + done + + post_state $HOSTNAME hardware-installing + + echo "Hardware installing with: $HOSTNAME" + run_chef $HOSTNAME + post_state $HOSTNAME hardware-installed + sleep 30 # Allow settle time + maybe_reboot;; + 3) post_state $HOSTNAME hardware-updating + run_chef $HOSTNAME + post_state $HOSTNAME hardware-updated + sleep 30 # Allow settle time + maybe_reboot;; +esac 2>&1 | tee -a /install-logs/$HOSTNAME-update.log diff --git a/crowbar/updates/parse_node_data b/crowbar/updates/parse_node_data new file mode 100755 index 00000000000..24d12704120 --- /dev/null +++ b/crowbar/updates/parse_node_data @@ -0,0 +1,70 @@ +#!/usr/bin/ruby + +require 'rubygems' +require 'json' +require 'getoptlong' + +# +# Parsing options can be added by adding to this list before calling opt_parse +# +@options = [ + [ [ '--help', '-h', GetoptLong::NO_ARGUMENT ], "--help or -h - help" ], + [ [ '--attribute', '-a', GetoptLong::REQUIRED_ARGUMENT ], "--attribute or -a - Name of hash object to return. Use . as sub-hash delimiter" ] +] + +@attributes = [] + + +def usage (rc) + puts "Usage: parse_node_data [options] " + @options.each do |options| + puts " #{options[1]}" + end + exit rc +end + +def help + usage 0 +end + + +### Start MAIN ### + +def opt_parse() + sub_options = @options.map { |x| x[0] } + lsub_options = @options.map { |x| [ x[0][0], x[2] ] } + opts = GetoptLong.new(*sub_options) + + opts.each do |opt, arg| + case opt + when '--help' + usage 0 + when '--attribute' + @attributes << arg + else + usage -1 + end + end + + if ARGV.length != 1 + usage -1 + end +end + +def main() + opt_parse + + hash = JSON.parse(File.open(ARGV[0]).read) + + @attributes.each do |attr| + data = hash + fields = attr.split(".") + fields.each { |x| data = data[x] unless data.nil? } + + puts "#{attr}=#{data}" + end + +end + +main + diff --git a/sledgehammer/CHANGE-LOG b/sledgehammer/CHANGE-LOG new file mode 100644 index 00000000000..080bdb80335 --- /dev/null +++ b/sledgehammer/CHANGE-LOG @@ -0,0 +1,2 @@ + +---- Initial Version ---- diff --git a/sledgehammer/HOWTO.Non.RedHat b/sledgehammer/HOWTO.Non.RedHat new file mode 100644 index 00000000000..dd4f2b1e728 --- /dev/null +++ b/sledgehammer/HOWTO.Non.RedHat @@ -0,0 +1,52 @@ +This file documents the process needed to create Sledgehammer on a non-Redhat +system. + +1: Download a CentOS 5.6 install CD or DVD from your favourite mirror. +2: Install a basic non-GUI, single-partition, no-LVM install in KVM using a + raw disk image. + a) Make a raw hard drive image to store the basic install: + qemu-img create -f raw centos.img 6G + b) Install CentOS on the raw image. Ensure that you use a single- + partition install without LVM, and do not install any desktop software. + kvm -m 512M -net none -hda centos.img -boot d -cdrom iso/CentOS-5.6-x86_64-bin-DVD-1of2.iso -usbdevice tablet + When you get to disk partitioning, select remove all partitions, and + select Remove and modify partitioning layout. + Delete all the logical volumes, then the volume group, then the + partitions. Once that is done. create a single partition that uses + all the space on the disk as the root filesystem, and force it to be a + primary partition. Ignore the warning about no swap space. +3: Once the install is finished, power off the VM, bind the disk image + to a loopback device, create a device for the partition using kpartx, + and mount the filesystem. + sudo losetup /dev/loop0 centos.img + sudo kpartx -a /dev/loop0 + mkdir centos_img centos + sudo mount /dev/mapper/loop0p1 centos_img + sudo cp -a centos_img/. centos/. + sudo umount centos_img + sudo kpartx -d /dev/loop0 + sudo losetup -d /dev/loop0 +4: Bind mount /proc, /sys, and the sledgehammer directory into the mounted + file system. + cd centos + sudo mkdir sledgehammer + sudo mount --bind /proc proc + sudo mount --bind /sys sys + sudo mount --bind /dev dev + sudo mount --bind "$SLEDGEHAMMER_DIR" sledgehammer +5: Copy over /etc/resolv.conf, and then chroot into the file system. + sudo cp /etc/resolv.conf etc/resolv.conf + sudo chroot . /bin/bash +6: Install the livecd-tools into the chroot following the instructions at + https://projects.centos.org/trac/livecd/wiki/GetToolset +7: In the sledgehammer directory in the chroot, run build_sledgehammer.sh + This will wind up creating bin/sledgehammer-tftpboot.tar.gz +8: Leave the chroot, and copy the tarball created in the last step to + wherever you configured build_crowbar.sh to find it. +9: Umount the bind mounts you created in step 4 + +All done, until sledgehammer needs to be rebuilt again. We don't anticipate +that happening too often -- Crowbar does all the heavy lifting, Sledgehammer +just provides an environment for us to discover hardware, configure the +RAID and BMC, and change whatever hardware settings we need to configure but +that we cannot to in Ubuntu for whatever reason. \ No newline at end of file diff --git a/sledgehammer/build_sledgehammer.sh b/sledgehammer/build_sledgehammer.sh new file mode 100755 index 00000000000..29ccc0a3495 --- /dev/null +++ b/sledgehammer/build_sledgehammer.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# +# This will need access to yum repositories. +# Make sure your yum system is setup to work. +# To run as non-root, you will need an sudoer entry that looks like this: +# user ALL=(ALL) NOPASSWD: /path/to/build_sledgehammer.sh +# +# Also SELinux needs to be turned off +# + +die() { local _r=$1; shift; echo "$@"; exit $1; } + +mkdir -p cache + +/usr/bin/livecd-creator --config=centos-sledgehammer.ks --cache=./cache -f sledgehammer || die "Couldn't build full iso image" +rm -fr tftpboot +/usr/bin/livecd-iso-to-pxeboot sledgehammer.iso || \ + die "Could not generate PXE boot information from Sledgehammer" +rm sledgehammer.iso + +mkdir -p bin || die -2 "Couldn't make bin directory" +tar czf bin/sledgehammer-tftpboot.tar.gz tftpboot + +chmod -R ugo+w bin +rm -rf tftpboot +exit 0 diff --git a/sledgehammer/buildenv/NOTES b/sledgehammer/buildenv/NOTES new file mode 100644 index 00000000000..0ed3adc1486 --- /dev/null +++ b/sledgehammer/buildenv/NOTES @@ -0,0 +1,11 @@ +# +# Need to make sure we are running a squashfs that works +# +# Make sure that you have the proper squashfs +# Run: +# yum reinstall squashfs-tools +# +# Run: +# cp -p /sbin/mksquashfs /sbin/mksquashfs.orig +# cp mksquashfs /sbin/mksquashfs +# diff --git a/sledgehammer/buildenv/mksquashfs b/sledgehammer/buildenv/mksquashfs new file mode 100755 index 00000000000..ed9ee06194d --- /dev/null +++ b/sledgehammer/buildenv/mksquashfs @@ -0,0 +1,4 @@ +#!/bin/bash + +# nuke any -no-progress arg, leave the rest unchanged. +/sbin/mksquashfs.orig "${@//-no-progress/}" diff --git a/sledgehammer/centos-sledgehammer.ks b/sledgehammer/centos-sledgehammer.ks new file mode 100644 index 00000000000..6e54d2516e8 --- /dev/null +++ b/sledgehammer/centos-sledgehammer.ks @@ -0,0 +1,106 @@ +lang en_US.UTF-8 +keyboard us +timezone US/Eastern +auth --useshadow --enablemd5 +selinux --disabled +firewall --disabled + +repo --name=a-base --baseurl=http://mirror.centos.org/centos/5/os/$basearch +repo --name=a-updates --baseurl=http://mirror.centos.org/centos/5/updates/$basearch +repo --name=a-extras --baseurl=http://mirror.centos.org/centos/5/extras/$basearch +repo --name=a-epel --baseurl=http://mirror.pnl.gov/epel/5/$basearch +repo --name=a-live --baseurl=http://www.nanotechnologies.qc.ca/propos/linux/centos-live/$basearch/live +repo --name=a-elff --baseurl=http://download.elff.bravenet.com/5/$basearch + +%packages +bash +kernel +syslinux +passwd +policycoreutils +chkconfig +authconfig +rootfiles +comps-extras +xkeyboard-config +dhclient +tcpdump +vim-enhanced +openssh-clients +OpenIPMI-tools +OpenIPMI +portmap +nfs-utils +procmail +wget +lshw +dmidecode +glibc.i686 +libxml2 +libxml2.i386 +compat-libstdc++-33.i386 +pciutils +ntp +chef +which +rpm +coreutils +tar +gzip +mktemp +libsysfs.x86_64 +libsysfs.i386 +yum +curl + +%post + +######################################################################## +# Create a sub-script so the output can be captured +# Must change "$" to "\$" and "`" to "\`" to avoid shell quoting +######################################################################## +cat > /root/post-install << EOF_post +#!/bin/bash + +echo ################################################################### +echo ## Creating the centos-live init script +echo ################################################################### + +cat > /etc/rc.d/init.d/openstack-start-up << EOF_initscript +#!/bin/bash +# +# live: Init script for live image +# +# chkconfig: 345 28 72 +# description: Init script for live image. + + +EOF_initscript + +/sbin/chkconfig --add openstack-start-up + +EOF_post + +/bin/bash -x /root/post-install 2>&1 | tee /root/post-install.log + + +%post --nochroot + +######################################################################## +# Create a sub-script so the output can be captured +# Must change "$" to "\$" and "`" to "\`" to avoid shell quoting +######################################################################## +cat > /root/postnochroot-install << EOF_postnochroot +#!/bin/bash + +cp *.gem $INSTALL_ROOT/root + +cp start-up.sh $INSTALL_ROOT/etc/rc.d/init.d/openstack-start-up +chmod +x $INSTALL_ROOT/etc/rc.d/init.d/openstack-start-up + +cp dhclient.conf $INSTALL_ROOT/etc + +EOF_postnochroot + +/bin/bash -x /root/postnochroot-install 2>&1 | tee /root/postnochroot-install.log + diff --git a/sledgehammer/dhclient.conf b/sledgehammer/dhclient.conf new file mode 100644 index 00000000000..1304c1b9b47 --- /dev/null +++ b/sledgehammer/dhclient.conf @@ -0,0 +1,12 @@ + +option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; +option dhcp-client-state code 225 = unsigned integer 16; +option dhcp-client-debug code 226 = unsigned integer 16; + +send host-name ""; +request subnet-mask, broadcast-address, time-offset, routers, + domain-name, domain-name-servers, domain-search, host-name, + netbios-name-servers, netbios-scope, interface-mtu, + rfc3442-classless-static-routes, ntp-servers, + dhcp-client-state, dhcp-client-debug; + diff --git a/sledgehammer/rest-client-1.3.1.gem b/sledgehammer/rest-client-1.3.1.gem new file mode 100644 index 00000000000..db34ca89273 Binary files /dev/null and b/sledgehammer/rest-client-1.3.1.gem differ diff --git a/sledgehammer/start-up.sh b/sledgehammer/start-up.sh new file mode 100755 index 00000000000..32b3edf2089 --- /dev/null +++ b/sledgehammer/start-up.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +set -x + +# Figure out where we PXE booted from. +bootif_re='BOOTIF=([^ ]+)' +ip_re='inet ([0-9.]+)/([0-9]+)' +ik_re='crowbar\.install\.key=([^ ]+)' +if [[ $(cat /proc/cmdline) =~ $bootif_re ]]; then + MAC="${BASH_REMATCH[1]//-/:}" + MAC="${MAC#*:}" + for nic in /sys/class/net/*; do + [[ -f $nic/address && -f $nic/type && \ + $(cat "$nic/type") = 1 && \ + $(cat "$nic/address") = $MAC ]] || continue + BOOTDEV="${nic##*/}" + break + done +fi +if [[ ! $BOOTDEV ]]; then + echo "We don't know what the MAC address of our boot NIC was!" + echo "We will assume we booted off eth0 and hope for the best." + BOOTDEV=eth0 + MAC=$(cat /sys/class/net/eth0/address) +fi + +# Make sure our PXE interface is up, then fire up DHCP on it. +ip link set "$BOOTDEV" up +dhclient "$BOOTDEV" + +if ! [[ $(ip -4 -o addr show dev $BOOTDEV) =~ $ip_re ]]; then + echo "We did not get an address on $BOOTDEV" + echo "Things will end badly." +fi +MYIP="${BASH_REMATCH[1]}" + +ADMIN_IP=$(grep dhcp-server /var/lib/dhclient/dhclient*.leases | \ + uniq | cut -d" " -f5 | cut -d";" -f1) +DOMAIN=$(grep "domain-name " /var/lib/dhclient/dhclient*.leases | \ + uniq | cut -d" " -f5 | cut -d";" -f1 | awk -F\" '{ print $2 }') +HOSTNAME_MAC="h${MAC//:/-}.${DOMAIN}" + +[[ $(cat /proc/cmdline) =~ $ik_re ]] && \ + export CROWBAR_KEY="${BASH_REMATCH[1]}" +HOSTNAME=$(hostname) + +[[ $HOSTNAME = localhost.localdomain ]] && { + hostname $HOSTNAME_MAC + HOSTNAME=${HOSTNAME_MAC} +} + +# enable remote logging to our admin node. +echo "# Sledgehammer added to log to the admin node" >> /etc/rsyslog.conf +echo "*.* @@${ADMIN_IP}" >> /etc/rsyslog.conf +service rsyslog restart + +# Setup common dirs +for d in updates install-logs; do + mkdir -p /$d + mount -t nfs $ADMIN_IP:/$d /$d +done + +export MAC BOOTDEV ADMIN_IP DOMAIN HOSTNAME_MAC HOSTNAME MYIP + +cd /updates +./control.sh