Powershell random password generator – with complexity!

Recently we needed to automate password randomization for account creation purposes. In order to ensure the password policy was met we needed, 8 characters total, 1 uppercase letter, 1 number, and 1 special character. We also needed to control the character set of special characters due to certain finicky applications. This script grabs a base password from http://random.org, verifies that it has all but our special character, and finally randomly inserts one of our special characters into the password.

Function Get-Seed{
# Generate a seed for future randomization
$RandomBytes = New-Object -TypeName 'System.Byte[]' 4
$Random = New-Object -TypeName 'System.Security.Cryptography.RNGCryptoServiceProvider'
$Random.GetBytes($RandomBytes)
[BitConverter]::ToInt32($RandomBytes, 0)
}

do
{
$initialPW = $(Invoke-RestMethod https://www.random.org/passwords/?num=1&len=7&format=plain&rnd=new) -replace "`n"

if($initialPW -cmatch "[A-Z]") { $containsUpper = $True } else { $containsUpper = $False }
if($initialPW -cmatch "[a-z]") { $containsLower = $True } else { $containsLower = $False }
if($initialPW -match "[0-9]") { $containsNumbr = $True } else { $containsNumbr = $False }

$specCharArray = [Char[]]"!#$&*_-"
$specChar = $($specCharArray | Get-Random -Count 1)

if (($containsUpper -eq $True) -and ($containsLower -eq $True) -and $($containsNumbr -eq $True))
{
$seed = $(Get-Random -Maximum 7 -Minimum 1 -SetSeed $(Get-Seed))
$initialPW.Insert($seed,$specChar)
break
}
}
While ($True)

Puppet: Passing a hash of variables to a defined type

One of the more interesting challenges I had recently involved a bit of automation with regard to user creation. If you’ve ever tried to automate resource creation with either defined types or classes, the nuances of puppet can be a bit of a pain, but they can also be very powerful. Here’s an example of a defined type that can execute an arbitrary number of commands with any number of variables (We just happen to be importing certificates in this example):

First the defined type:

define import_certs($cert_path, $cert_name) {
  exec {"Import $cert_name":
    command => "/usr/bin/certutil -A -d /path/to/certificate/db -a -i ${cert_path} -t C,C,C -n \"${cert_name}\"",
  }
}

As you can see the type accepts 2 variables, $cert_path and $cert_name. Certutil requires a friendly name and this allows us to name our certificates appropriately. NOTE: that double quotes are required for variable interpolation (This bit me a couple of times). Now let’s take a look at how to pass a hash to this defined type so that we can import as many certificates as needed:

In your declaration, you end up creating a hash of hashes and pass the defined type the entire hash of hashes using “create_resources”:

$my_certificates = {
  certificate_file_1 => { cert_path => '/path/to/certificate/file', cert_name => 'Friendly Certificate Name' },
  certificate_file_2 => { cert_path => '/path/to/certificate/file', cert_name => 'Friendly Certificate Name' },
  certificate_file_n => { cert_path => '/path/to/certificate/file', cert_name => 'Friendly Certificate Name' },
}

create_resources(import_certs, $my_certificates)

As you can see, this is a quick and dirty way to shove a lot of certificates into a database (For a browser perhaps). Of course the limits are boundless, I know this will come in handy again in the future so I put it on <virtual> paper.

Open vSwitch: basic initial setup on Ubuntu 12.04

Software defined networking is all the rage these days in IT, and Open vSwitch is where most people get their first introduction. Whether you’re running KVM or Xen, Open vSwitch will allow you to abstract network setup functions from the hypervisor. Here’s a quick guide to setting up Open vSwitch and replacing a physical interface with a virtual one:
Continue reading

haproxy + keepalived – the free HA load balancer

Load balancers are cool, especially free ones. haproxy and keepalived together can give you a simple HA load balancer at the cost of the hardware you run it on. Here’s how to setup a basic active/passive load balancer with haproxy and keepalived. First the environment:
Continue reading

Galera / WSREP – MySQL cluster on Ubuntu 12.04 LTS – basic initial setup

mysql-logoIt took me about 4 hours of actual research, and trial and error, however I finally got a working Galera based WSREP MySQL cluster up and running. Here’s what it took and how the pieces came together:

Getting the install right took a couple of hours by itself for some reason, here’s the order that worked best on Ubuntu 12.04 LTS:
Continue reading

OpenStack API python script example

Recently I was given a number of API test cases for OpenStack to manually execute via curl. It was quite tedious figuring out the nuances of the OpenStack API using curl; manually constructing HTTP headers and substituting JSON values into a curl command got old real fast. I decided to run all the test cases through python, this was the first bit I had to finish, authentication!

OpenStack API call requesting a token. Tokens are valid for authentication for 24 hours:

#!/usr/bin/python

import argparse
import getopt
import json
import sys
import urllib2

def getToken(url, osuser, ostenant, ospassword):

    """
    Returns a token to the user given a tenant,
    user name, password, and OpenStack API URL.
    """

    url = url + '/tokens'
    tokenRequest = urllib2.Request(url)
    tokenRequest.add_header("Content-type", "application/json")
    jsonPayload = json.dumps({'auth' : {'tenantName' : ostenant, 'passwordCredentials' : {'username' : osuser, 'password' : ospassword}}})
   
    request = urllib2.urlopen(tokenRequest, jsonPayload)
    json_data = json.loads(request.read())
   
    request.close()
    return json.dumps(json_data)

# Build our required arguments list
parser = argparse.ArgumentParser()
mandatory = parser.add_argument_group("mandatory")
mandatory.add_argument("-l", "--login", help="The administrative user for your OpenStack installation", type=str)
mandatory.add_argument("-p", "--password", help="The administrative user's password", type=str)
mandatory.add_argument("-t", "--tenant", help="The administrative user's tenant / project", type=str)
mandatory.add_argument("-u", "--url", help="The Keystone API endpoint from running, 'nova endpoints'", type=str)
args = parser.parse_args()

# Validate arugments were given
if type(args.url) != type(str()):
    sys.stderr.write('Invalid URL: %s\n' % args.url)
    parser.print_help()
    sys.exit(2)
if type(args.tenant) != type(str()):
    sys.stderr.write('Invalid tenant: %s\n' % args.tenant)
    parser.print_help()
    sys.exit(2)
if type(args.password) != type(str()):
    sys.stderr.write('Invalid password: %s\n' % args.password)
    parser.print_help()
    sys.exit(2)
if type(args.login) != type(str()):
    sys.stderr.write('Invalid login: %s\n' % args.login)
    parser.print_help()
    sys.exit(2)
   
# Since we return a raw JSON payload from getToken,
# we need to load it into a readable object.
adminToken = json.loads(getToken(args.url, args.login, args.tenant, args.password))

# Access the token portion of the JSON payload and grab the token and tenant ID
adminTokenID = adminToken['access']['token']['id']
adminTokenTenantID = adminToken['access']['token']['tenant']['id']

for item in adminToken['access']['serviceCatalog']:
    """
    The "name" of each OpenStack service catalog item from
    the item list changed between versions.  Things like
    "glance" became "volume" and "keystone" became "identity".  
    You will need to update this based on your installation.
    """

    if item['name'] == "nova":
        adminNovaURL = item['endpoints'][0]['adminURL']
    if item['name'] == "glance":
        adminGlanceURL = item['endpoints'][0]['adminURL']
    if item['name'] == "nova-volume":
        adminVolumeURL = item['endpoints'][0]['adminURL']
    if item['name'] == "ec2":
        adminEc2URL = item['endpoints'][0]['adminURL']
    if item['name'] == "swift":
        adminSwiftURL = item['endpoints'][0]['adminURL']
    if item['name'] == "keystone":
        adminAuthURL = item['endpoints'][0]['adminURL']

print "------ Admin information ------"
print "Token ID = ", adminTokenID
print "Tenant ID = ", adminTokenTenantID
print "Nova URL = ", adminNovaURL
print "Glance URL = ", adminGlanceURL
print "Volume URL = ", adminVolumeURL
print "EC2 URL = ", adminEc2URL
print "Swift URL = ", adminSwiftURL
print "Keystone URL = ", adminAuthURL
print "------ End Admin info ---------"

When passed the appropriate parameters, this script will print your environment information in a nice readable format:

uesr@localhost:~$ ./token.py -l admin -t admin -p password -u http://localhost:35357/v2.0
------ Admin information ------
Token ID =  abc123def456ghi789jkl012mno345pq
Tenant ID =  qp543onm210lkj987ihg654fed321cba
Nova URL =  http://localhost:8774/v2/qp543onm210lkj987ihg654fed321cba
Glance URL =  http://localhost:9292/v1
Volume URL =  http://localhost:8776/v1/qp543onm210lkj987ihg654fed321cba
EC2 URL =  http://localhost:8773/services/Admin
Swift URL =  https://localhost:8080/v1/
Keystone URL =  http://localhost:35357/v2.0
------ End Admin info ---------

Hack the planet automobile?

I’ve been complaining to the wife that her car’s infotainment system is behind the curve a bit. This is an attempt to help spread the word about Automotive Grade Linux (AGL), and perhaps aid the open-source automotive community a bit. Here’s an excerpt from an article posted on wired:


The major roadblock to the rapid adoption of technology in cars has always been vehicles’ long production cycles. But automakers are also guilty of hampering wide-scale innovation with their proprietary approach to infotainment systems and software, while dragging their feet on a collaborative, open source solution that would benefit consumers and car tech overall.

Read the full post here.

/etc/security/limits.conf nofile absolute maximum

Apparently unlimited != unlimited in the Linux kernel for maximum number of open files. After some extensive digging, I finally found the actual maximum to the nofile setting in /etc/security/limits.conf. Yes I was searching in the context of Oracle (Imagine that) for a maximum number of procs / files. The Linux kernel has a hard upper limit of 1024*1024 (1048576 – a magical number I won’t soon forget).

Consider the following:

[root@localhost ~]# grep mrsmith /etc/security/limits.conf
mrsmith     soft    nofile      unlimited  
mrsmith     hard    nofile      unlimited

Trying to su to mrsmith suddenly casts you aside:

[root@localhost ~]# su - mrsmith
could not open session

Displaying the open file setting for mrsmith shows something odd:

[root@localhost ~]# ulimit -au mrsmith
...
open files                      (-n) 1024

Why would our number of open files be 1024? This apparently is the default setting for users without a custom nofile set based on the following bugzilla.

Then what is our actual upper bound for nofile in /etc/security/limits.conf? This isn’t well documented anywhere I’ve seen, but based on some research it appears to be 1024*1024. Setting nofile to 1048577 produces the same “could not open session” error as mentioned previously. 1048576 however seems to work just fine. Although you can set your nofile ulimit to this, the effects of such are unknown and I would highly recommend testing any such setting.

kvm-qemu: scripted clones of virtual machines

KVM is quickly becoming a viable competitor to paid vendors given the wide variety of features it offers in comparison (Memory page sharing, live migration, HA clustering, the list goes on and on…). Cloning virtual machines is common practice when deploying large environments with many virtual machines. Here’s a quick and dirty way to clone a virtual machine that could easily be scripted to deploy a large number of virtual machines:
Continue reading

vSphere PowerCLI – slot metrics

We’re not talking about coin slots here people! vSphere 5 uses slot sizes to determine the capacity for high availability (HA) fail-over metrics. Here’s a quick and dirty way to see what your current slot size is:

Connect-VIServer <vCenterServerName>
$Cluster = Get-Cluster -Name <clusterName>
$SlotDetails = $Cluster.ExtensionData.RetrieveDasAdvancedRuntimeInfo()
Write-Host -ForegroundColor Green "`n Slot info for <clusterName> cluster `
`n Number of vCPUs per slot: $($SlotDetails.SlotInfo.NumvCpus) `
`n MHz per slot: $($SlotDetails.SlotInfo.CpuMHz) `
`n Memory (MB) per slot: $($SlotDetails.SlotInfo.MemoryMB) `
`n Total Slots = $($SlotDetails.TotalSlots) `
`n Used Slots = $($SlotDetails.UsedSlots) `
`n Available Slots = $($SlotDetails.TotalSlots - $SlotDetails.UsedSlots)"

Be sure and replace <vCenterServerName> with your vCenter server name and <clusterName> with the HA cluster name for which you need slot information. This can be useful when determining your current fail-over capacity, just be sure your slot size is adequate for your environment. You should see output similar to the following:

Name                           Port  User
----                           ----  ----
<vCenterServerName>            443   <user>

 Slot info for <clusterName> cluster
 Number of vCPUs per slot: 2
 MHz per slot: 500
 Memory (MB) per slot: 1024
 Total Slots = 250
 Used Slots = 32
 Available Slots = 218

PowerCLI C:\temp>

Thanks to Alan over at http://www.virtu-al.net/

OpenLDAP basic initial setup on CentOS 6.2

When I started setting up openldap for centralized authentication purposes I was surprised at the lack of “basic” documentation on the subject. There are several “getting started” guides, however most of them are documented using slapd.conf instead of cn=config. Here’s what you need to setup openldap for authentication using the new cn=config ldap database on CentOS 6.2:

First you’ll need to install the client and server packages for openldap:

[root@openldap-server ~]# yum -y install openldap-servers openldap-clients

This installs all the necessary tools and daemons (slapd) you need to get a base directory setup that you can later modify with your favorite ldap editor.
Continue reading

RHEL extended file and folder permissions (ACLs) using setfacl and getfacl

I’ve heard several people say they thought the application of access control lists (ACLs) in Linux lacked something to be desired. It’s obvious they didn’t know about the setfacl and getfacl tools available in RHEL. Extremely granular file and folder permission structures are possible with these tools and here I’ll give you an example of how to accomplish basic ACL structures that you might not have previously thought possible:
Continue reading

RHEL chkconfig service script example

I’ve put several of these together and a few people have asked me for a generic chkconfig script so here goes. You’ll need to replace /path/to/servicename and anything that says servicename with the actual path of what you want to run. Save the file in /etc/init.d as what you want the <service name> to be then simply run:

# chkconfig --add <service name>

#!/bin/bash
#
# chkconfig: 35 90 12
# description: My server
#

#Source function library.
. /etc/init.d/functions

start() {
        echo -n "Starting my server: "
        daemon /path/to/servicename
         ### touch the lock file ###
        touch /var/lock/subsys/servicename
        success $"My server startup"
        echo
}

stop() {
        echo -n "Stopping my server: "
        killproc servicename
         ### Remove the lock file ###
        rm -f /var/lock/subsys/servicename
        echo
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status servicename
        ;;
  restart|reload|condrestart)
        stop
        start
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart|reload|status}"
        exit 1
esac
exit 0

how-to: cloning a boot from SAN RHEL LUN

Some prep work goes into setting up a good boot from SAN LUN for use in deploying new physical systems however once you have a well defined structure of LVM and standard disks, the process of cloning a boot LUN can easily be scripted. Here’s the basic steps that go into updating your kernel once you’ve cloned a boot disk.
Some things to keep in mind when preparing a LUN to clone to other systems:

  1. Try to keep the number of LUNs required for the system to boot to 1. This will ensure there are less edits required to update your initrd image.
  2. Avoid cloning a LUN that has multiple volume groups with more than 1 SAN LUN. As of RHEL 5.7 (2.6.18-274.x kernel) udev has problems utilizing a system volume group when say and application volume group (with separate physical disks) is missing.
  3. Prior to cloning your system, update your multipath.conf so that your bindings file is located on the root partition and not on a separate partition as so:
    bindings_file /etc/bindings

Post clone steps for updating your initrd image with the scsi ID for your new boot disk:
Continue reading

putty x11 forwarding stops working after su

After finding this once, scouring the web again trying to find the solution, I figured this time I would take note. If you ever have to forward x11 locally using putty, you’ve probably noticed that it doesn’t work after switching users with su. Here’s what you need to do to fix this:

First try to forward xclock

[user@localhost ~]$ xclock
Xlib: connection to "localhost:10.0" refused by server
Xlib: PuTTY X11 proxy: MIT-MAGIC-COOKIE-1 data did not match
Error: Can't open display: localhost:10.0

Notice the MIT-MAGIC-COOKIE-1 error – next let’s see what xauthorization is setup. Drop back down to the user you su’d from:

[user@localhost ~]$ exit
[root@localhost ~]# xauth list
localhost/unix:10  MIT-MAGIC-COOKIE-1  1234567890abcdefghijklmnopqrstuv

Now let’s add this “magic cookie” to the authorized x11 terminals list for the user we need to su to:

[root@localhost ~]# su - user
[user@localhost ~]$ xauth add localhost/unix:10  MIT-MAGIC-COOKIE-1  1234567890abcdefghijklmnopqrstuv
[user@localhost ~]$ xclock

Now your forwarding should work.

Quantum Levitation – bring on hoverboards!

This is pretty flippin’ sweet – when I saw the video the first thing I thought of was Back to the Future II and hoverboards! Anyway, this is a nice jolt of science catching up to science fiction in my opinion although something tells me we won’t see commercial applications for some time.

Weblogic Linux service script

Seeing as how a ton of the work I do these days is with Weblogic, I figured it would come in handy to have an init script tucked away for a rainy day. Recently we had an Oracle consultant in to deploy Oracle Business Intelligence, Enterprise Edition and while I can say the consultant knew too little about systems, in the same breath I would say he knows too much about OBIEE. He put together an init script for OBIEE which is based on Weblogic so I borrowed and mangled what he started with to fit my needs and this was the outcome.

#!/bin/bash
# Weblogic start / stop script.
#
# chkconfig: 345 99 10
# description: Manage Weblogic services.
#


SRVC_OWNR=<user>                    # Local Unix user running Weblogic
WL_BIN=/path/to/Oracle/Middleware   # Deployment root directory
WL_USER=wl_admin_user               # WL administrator name
WL_PASSWD=<password>                # WL administrator password
WL_DOMAIN=<wl_domain>               # Domain name
Mgmt_SERVER=<admin_server>          # Admin server name
MS_SERVER=<ms1>                     # Managed server name
ADMIN_URL=<host>:<port>             # Admin server URL (hostname:port)
MS_URL=<host>:<port>                # Managed server URL (hostname:port)
WL_PATH=$WL_BIN/wlserver_10.3/server/bin
WL_DOMAIN_PATH=$WL_BIN/user_projects/domains/$WL_DOMAIN/bin
export WL_DOMAIN_PATH

START_LOG=/var/log/wl-start.log
STOP_LOG=/var/log/wl-stop.log
SUBSYS=wl_svcs

start() {

    echo "********************************************************************************"
    echo "Starting Node manager on $(date)"
    echo "********************************************************************************"
    su $SRVC_OWNR -c "$WL_PATH/startNodeManager.sh" &
    wait_for "Secure socket listener started on port 5556"

    echo "********************************************************************************"
    echo "Starting Admin Server on $(date)"
    echo "********************************************************************************"
    su $SRVC_OWNR -c "$WL_DOMAIN_PATH/startWebLogic.sh" &
    wait_for "Server started in RUNNING mode"

    echo "********************************************************************************"
    echo "Starting Managed Server $MS_SERVER on $(date)"
    echo "********************************************************************************"
    su $SRVC_OWNR -c "$WL_DOMAIN_PATH/startManagedWebLogic.sh $MS_SERVER http://$ADMIN_URL" &
    wait_for "Server started in RUNNING mode"

    echo "********************************************************************************"
    echo "Weblogic start sequence completed on $(date)"
    echo "********************************************************************************"
}
stop() {

    echo "********************************************************************************"
    echo "Stopping Managed Server $MS_SERVER on $(date)"
    echo "********************************************************************************"
    su $SRVC_OWNR -c "$WL_DOMAIN_PATH/stopManagedWebLogic.sh $MS_SERVER t3://$ADMIN_URL $WL_USER $WL_PASSWD"

    echo "********************************************************************************"
    echo "Stopping Admin Server $Mgmt_SERVER on $(date)"
    echo "********************************************************************************"
    su $SRVC_OWNR -c "$WL_DOMAIN_PATH/stopWebLogic.sh"

    echo "********************************************************************************"
    echo "Stopping Node manager on $(date)"
    echo "********************************************************************************"
    su $SRVC_OWNR -c "kill -9 $( ps -ef| grep weblogic.NodeManager | grep -v grep | awk '{print $2}' )"

    echo "********************************************************************************"
    echo "Weblogic stop sequence completed on $(date)"
    echo "********************************************************************************"
}

wait_for() {
    res=0
    while [[ ! $res -gt 0 ]]
    do
        res=$(tail -5 "$START_LOG" | fgrep -c "$1")
        sleep 5
    done
}

case "$1" in
    start)
        echo "********************************************************************************"
        echo "Starting Weblogic Components on $(date)"
        echo "Logs are sent to $START_LOG"
        echo "********************************************************************************"
        start &> $START_LOG &
        touch /var/lock/subsys/$SUBSYS
    ;;
    stop)
        echo "********************************************************************************"
        echo "Stopping Weblogic Components on $(date)"
        echo "Logs are sent to $STOP_LOG"
        echo "********************************************************************************"
        stop &> $STOP_LOG
        rm -f /var/lock/subsys/$SUBSYS
        ;;
    *)
        echo "Usage: $(basename $0) start|stop"
        exit 1
esac

exit 0

If you’ve ever worked with Weblogic the script should be fairly self explanatory. The script starts node manager, waits for completion, then moves on to an admin server and managed server; comment one or the other out based upon your needs.

bash: reverse the contents of a file

This was a unique interview question I had a while back, I was asked to write a script that would write the contents of a file to another file in reverse order. Now it seems trivial so I thought I would post a basic example:

#!/bin/bash

linecount=`cat file1 | wc -l`

for (( i=1; i<=$linecount; i++ ))
do
  /usr/bin/tail -$i file1 | /usr/bin/head -1 >> file2
done

It’s really that simple! First you grab the line count of your source file (Line 3) and based on the number of lines in the file, we loop and iterate backwards using tail + head (Line 7).

how to: cookies with javascript

I love cookies – that’s all the personality you’re going to get for this post. Here’s the javascript / html form pair you need to create and manage browser cookies:

function SetCookie(){

if (document.cookie && document.cookie != "") {
// Cookie exists - let's append to it!

// Grab "Field 1" from the form and assign it to item1
    var item1=document.getElementById('ctest0');
// Grab "Field 2" from the form and assign it to item2
    var item2=document.getElementById('ctest1');
    var cookie=document.cookie;

/*
Get the actual text from the fields via the item# variables .value property
and assign it to a string that we can later split via the # sign in the cookie
itself!
*/

    var newCookie=cookie + "#field1=" + escape(item1.value);
    newCookie=newCookie + "#field2=" + escape(item2.value);
   
// Generate the cookie
    document.cookie=newCookie;
   
}
else
{
// Cookie does not exists - create a new one!

// Same as above except we also name our cookie
    document.cookie="name=stuff";

// Grab "Field 1" from the form and assign it to item1
    var item1=document.getElementById('ctest0');
// Grab "Field 2" from the form and assign it to item2
    var item2=document.getElementById('ctest1');
    var cookie=document.cookie;

/*
Get the actual text from the fields via the item# variables .value property
and assign it to a string that we can later split via the # sign in the cookie
itself!
*/

    var newCookie=cookie + "#field1=" + escape(item1.value);
    newCookie=newCookie + "#field2=" + escape(item2.value);
   
// Generate the cookie
    document.cookie=newCookie;
}  

}

function GetCookie(){

/*
Cookie fields are stored in item=value pairs.  We separate the fields
by a hash sign in the SetCookie() function therefore we'll split the
entire cookie into an array of item=value pairs via the cookieArray
variable assignment.  Then we have each field in a item=value pair as
data inside each array element.  Next you'll need to parse the array
as done in the for loop below.
*/


var cookieToText,cookieName,fieldData,cookieArray=document.cookie.split('#');
// Field counter - used for display purposes

// For loop to parse through each item=value pair in the cookie
for (i=0;i<cookieArray.length;i++)
    {
    if (i=="0")
        {
       
        /*
        The first field in a cookie is always the cookie name.  Let's add that
        to our "cookie to text" conversion variable for ease of use!  Also we
        know the name of the cookie since we set it previously.  The word "Stuff"
        is 5 characters long thus we strip out 5 characters from the second field
        (The data) in the item=value pair.
        */

        cookieName="Cookie Name: "
        // Split the item=value pair
        fieldData=cookieArray[i].split('=');
        // Grab the name of the cookie
        cookieToText=cookieName + fieldData[1].substr(0,5) + "\n";
                       
        }
        else
        {
        /*
        These are data items.  Let's put them on top of the "cookie to text"
        conversion variable for later display.  You have to "unescape" cookie
        data as it is encoded.
        */

       
        fieldData=cookieArray[i].split('=');
        cookieToText=cookieToText + "Field " + i + ": " + unescape(fieldData[1]) + "\n"
                       
        }

}
// Finally - display the "cookie to text" conversion variable:
alert(cookieToText);
}

Here’s the form you need to test your javascript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test</title>
<script type="text/javascript" src="cookie.js"></script>
<form name="srf" id="srf" method="post" action=""> 
Field 1: <input id="ctest0" name="ctest0" type="text" maxlength="255" value=""/><br/>  
Field 2: <input id="ctest1" name="ctest1" type="text" maxlength="255" value=""/><br/>
<input id="ctest2" type="button" name="setCookie" value="Set Cookie" onclick="SetCookie()"/><br/>  
<input id="ctest3" type="button" name="getCookie" value="Get Cookie" onclick="GetCookie()"/><br/>  
</form>
</html>