Friday, January 9, 2015

How to Install VMware Workstation on Linux and Create Virtual Machine

VMware Workstation is VMware’s commercial virtualization software for x64 computers.
This is a Type 2 hypervisor, which means it is installed on top of an existing operating system running on the computer hardware called the host operating system. It can be installed on both Linux and Windows host Operating Systems.
In VMware Workstation you can set up multiple virtual machines, each with its own guest operating system, and execute them simultaneously.

Supported guest operating systems can be 32 bit or 64 bit, including most Linux distros, most versions of Windows, and other OS like FreeBSD and Solaris.
This article will show how to install VMware Workstation 10 on two popular Linux distributions, CentOS 6.4 and Ubuntu 12.04 LTS, setup virtual machines for installing guest operating systems.

I. Install VMware Workstation

Download VMware Workstation

Download the 30 day trial version of VMware Workstation from here. The full version can be purchased from the VMware online store.
VMware Workstation comes with one install file for all Linux distributions, which is a zipped .bundle file. The latest stable release of VMware Workstation is 10.0.

Launch VMware Workstation Installer

To install VMware Workstation open a Terminal and cd to the directory containing the zipped VMware Workstation install file.
Unzip the install file from the downloads directory, and give execute permission for the user.
cd Downloads

unzip VMware-Workstation-Full-10.0.0-1295980.x86_64.zip

chmod 755 VMware-Workstation-Full-10.0.0-1295980.x86_64.bundle
On CentOS 6.4, execute the .bundle as shown below:
$ su -
Password: your password

# ./VMware-Workstation-Full-10.0.0-1295980.x86_64.bundle
On Ubuntu 12.04, execute the .bundle as shown below:
$ sudo ./VMware-Workstation-Full-10.0.0-1295980.x86_64.bundle
This will launch the installation process.

Accept the license terms

First, it will display the welcome screen with the end user license agreement. Accept the license and click on “Next”.
Next, it will display another license agreement for “VMware OVF Tool component for Linux”. Accept the license and click on “Next”.

Default Values

Next, it will ask the following questions. In most cases, just select the default values.
  • Check for product update on startup. Say “Yes”.
  • Send anonymous system data and usage statistics to VMware. Say “No”.
  • Enter the user that will initially connect to Workstation server. Type “root”.
  • Directory for shared viritual machines. Default at “/var/lib/vmware/Shared VMs”
  • HTTPS port. Default value 443.

License Key

Since this is a trial version of the software, you can skip this step. Just leave the license key field empty, and click on “Next”.

Finish the Install

Finally it will tell you the product is ready to be installed. Click Install to begin the installation.
After few mins, it will display the “installation was successful” message. Click on the “Close” button.

II. Create Virtual Machine from Workstation

Launch VMware Workstation

On CentOS you can launch VMware Workstation from Applications -> System Tools -> VMware Workstation.

On Ubuntu you can launch VMware Workstation from the Unity Dash as shown below.

On the VMware Workstation Home screen you can choose to Create a New Virtual Machine or Open an existing Virtual Machine.

Choose Configuration Type

To create a new virtual machine click Create a New Virtual Machine on the home screen or click “File” -> New Virtual Machine.
The New Virtual Machine Wizard will launch. You can choose a Typical configuration or a Custom configuration. The default is “Typical configuration”.

Install Guest OS

You can choose to install a guest operating system now from either a physical CD/DVD drive or an ISO image, or you may choose to install the guest operating system later.

Choose Guest OS

In this example we will install an Ubuntu 12.04 guest operating system on VMware Workstation.

Virtual Machine Name

Specify the virtual machine name, and location. The default values will be populated automatically based on selections from previous section.

Disk Size

In this screen you can specific the disk size for this VM. Also select “split virtual disk into multiple files” radio-button.

Create VM

Finally, it will display a summary screen with all your selections to create this particular VM. Review this and click on “Finish” to create this VM.
You’ll get a “Virtual Machine Created” confirmation screen. Click on “Close”.

New VM Tab

This newly created Ubuntu guest VM will get its own tab next to the Home tab as shown below.

Install Guest OS

To install the guest operating system click Edit virtual machine settings.
Here you can change various settings of the virtual machine the guest operating system will be installed on, such as Memory size etc.
Click on CD/DVD (SATA). Make sure Connect at power on is ticked and select Use ISO image. Browse to the location of the Ubuntu ISO file and click on Open.
Click Save to save the virtual machine configuration and close the Virtual Machine Settings window, On the Ubuntu virtual machine tab click on Start up this guest operating system, and install the guest operating system. After the installation is finished and the guest operating system is started.

How to Delete Old Obsolete and Expired Oracle RMAN Backup

If you are sysadmin, sometimes you might find yourself dealing with Oracle backups.
Apart from taking oracle RMAN backup, you should also understand how to Delete the old backups from the RMAN catalog and from physical filesystem.
If you don’t properly delete obsolete and expired Oracle DB backup from the catalog, it will cause some unnecessary throw error message during backup and also it will take-up space at the filesystem level.
Also, it is not recommended to directly remove the RMAN backup files from the filesystem using Linux rm command.
This tutorial explains how to identify the backups that are obsolete and expired, and how to properly delete them from RMAN.

The main parameter that decides what to delete is the retention policy. To identify your retention policy, connect using RMAN and and execute “show all” and look for the following line.

RMAN Retention Policy

In the following example, retention policy is 4 days. So, any backup that is older than 4 days is considered obsolete and old.
$ rman target /
RMAN> SHOW ALL;

CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 4 DAYS;
There is a big difference between Obsolte and Expired RMAN backup. We’ll explain both in this article.

I. Delete Obsolete Backup

1. What is an Obsolete Backup?

In our example above, any backup that we have in our system (both on RMAN catalog, and as physical RMAN backup files at the filesystem level) that is older than 4 days is considered obsolete. Please keep in mind that sometimes it might not be just 4 days. It depends on whether a full-backup is available within the last 4 days. If you don’t have a full backup in the last 4 days, then what RMAN considers as obsolete will be even longer than that. i.e Until the last full backup.
Obsolete backups are those that are not required to satisfy RMAN requirement of what is specified in the retention policy to recover the database from the backup.
The following three things will happen when you perform “DELETE OBSOLETE” from RMAN prompt:
  1. The physical backup files are removed from the filesystem level (or from tape backup)
  2. The backup entries are removed from the RMAN recovery catalog
  3. The entries are marked as DELETED in the Oracle control file

2. View Backups Before Delete Obsolete

In the following example, I see that there are lot of backup that I have on the system that are way older than what I need to satisfy my recovery requirement. i.e I have backups starting from 6th June until 26th Sep (several months).
RMAN> LIST BACKUP SUMMARY;

List of Backups
===============
Key     TY LV S Device Type Completion Time #Pieces #Copies Compressed Tag
------- -- -- - ----------- --------------- ------- ------- ---------- ---
624     B  F  A DISK        06-JUN-14       1       1       YES        TAG20140606T220138
625     B  F  A DISK        06-JUN-14       1       1       YES        TAG20140606T220138
626     B  F  A DISK        06-JUN-14       1       1       NO         TAG20140606T221610
627     B  F  A DISK        13-JUN-14       1       1       YES        TAG20140613T220147
..
..
666     B  F  A DISK        05-SEP-14       1       1       YES        TAG20140905T220151
667     B  F  A DISK        12-SEP-14       1       1       YES        TAG20140912T220151
668     B  F  A DISK        19-SEP-14       1       1       YES        TAG20140919T220152
669     B  F  A DISK        26-SEP-14       1       1       YES        TAG20140926T220206

3. Perform RMAN CrossCheck

Before we start executing the delete obsolete command, it is always recommended to do a crosscheck of the backup as shown below.
crosscheck backup command will check for the records in the RMAN repository to make sure they are accurate. If there is an record in the RMAN catalog that is not available on the physical filesystem, it will make that entry with appropriate status.
RMAN> CROSSCHECK BACKUP;

allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=37 device type=DISK
allocated channel: ORA_DISK_2
channel ORA_DISK_2: SID=22 device type=DISK
crosschecked backup piece: found to be 'AVAILABLE'
backup piece handle=/rman-backups/dev-db/full_okpa6ke3_788_1 RECID=642 STAMP=849564099
crosschecked backup piece: found to be 'AVAILABLE'
..
..
backup piece handle=/rman-backups/dev-db/full_qvpiu46h_863_1 RECID=686 STAMP=858722513
crosschecked backup piece: found to be 'AVAILABLE'
backup piece handle=/rman-backups/dev-db/full_r4pjgir0_868_1 RECID=687 STAMP=859327328
Crosschecked 46 objects

4. Delete Obsolete RMAN backup

Once the crosscheck is done, it is time to delete the old obsolete backup using the DELETE OBSOLETE command as shown below.
RMAN> DELETE OBSOLETE;

RMAN retention policy will be applied to the command
RMAN retention policy is set to recovery window of 4 days
using channel ORA_DISK_1
using channel ORA_DISK_2
Deleting the following obsolete backups and copies:
Type                 Key    Completion Time    Filename/Handle
-------------------- ------ ------------------ --------------------
Backup Set           625    06-JUN-14
  Backup Piece       643    06-JUN-14          /rman-backups/dev-db/full_ojpa6ke3_787_1
Backup Set           624    06-JUN-14
  Backup Piece       642    06-JUN-14          /rman-backups/dev-db/full_okpa6ke3_788_1
..
..
Backup Set           664    05-SEP-14
  Backup Piece       682    05-SEP-14          /rman-backups/dev-db/full_qjphp6ug_851_1
Backup Set           667    12-SEP-14
  Backup Piece       685    12-SEP-14          /rman-backups/dev-db/full_qqpiblig_858_1

Do you really want to delete the above objects (enter YES or NO)? YES
Once you say “YES” to the above confirmation, then it will start deleting those obsolete backups.
backup piece handle=/rman-backups/dev-db/full_ojpa6ke3_787_1 RECID=643 STAMP=849564099
deleted backup piece
backup piece handle=/rman-backups/dev-db/full_okpa6ke3_788_1 RECID=642 STAMP=849564099
deleted backup piece
..
..
backup piece handle=/rman-backups/dev-db/full_qjphp6ug_851_1 RECID=682 STAMP=857512912
deleted backup piece
backup piece handle=/rman-backups/dev-db/full_qqpiblig_858_1 RECID=685 STAMP=858117712
Deleted 40 objects
WARNING: This will also physically delete the RMAN backup files from the filesystem. So, be careful and know exactly what you are doing before you execute this command.

5. Other Delete Obsolete Options

If you are writing a shell script that will automatically do this for you on an on-going basis, you don’t want to manually say “YES” to delete obsolete command as shown above.
Instead, you can ignore the prompt and automatically delete all obsolete backups as shown below.
RMAN> DELETE NOPROMPT OBSOLETE;
Also, if you want to delete obsolete backup based on your own recovery window criteria (instead of what is configured in RMAN when you do “show all”), you can specify it as shown below. The following will delete old backups based on recovery window of 10 days.
RMAN> DELETE OBSOLETE RECOVERY WINDOW OF 10 DAYS;

6. View Backups After Delete Obsolete

Finally, if you do the list backup summary, you’ll notice that this has only the backups that are required to satisfy the recovery criteria. All other backups are deleted.
RMAN> LIST BACKUP SUMMARY;

List of Backups
===============
Key     TY LV S Device Type Completion Time #Pieces #Copies Compressed Tag
------- -- -- - ----------- --------------- ------- ------- ---------- ---
661     B  F  A DISK        29-AUG-14       1       1       YES        TAG20140829T220152
663     B  F  A DISK        29-AUG-14       1       1       NO         TAG20140829T221624
665     B  F  A DISK        05-SEP-14       1       1       YES        TAG20140905T220151
666     B  F  A DISK        05-SEP-14       1       1       YES        TAG20140905T220151
668     B  F  A DISK        19-SEP-14       1       1       YES        TAG20140919T220152
669     B  F  A DISK        26-SEP-14       1       1       YES        TAG20140926T220206

II. Delete Expired Backup

1. What is an Expired Backup?

When you have an entry in the RMAN repository for a backup, but there are no corresponding physical rman backup files at the filesystem level, that is considered as expired entry.
But, you need to execute the crosscheck command, which will go through all the records in the RMAN catalog, and mark any expired records appropriately.

2. Perform RMAN CrossCheck

Sometimes when you try to execute the delete command, you might get the following “RMAN-06207″ error message, when there is a mis-match of the status for the records in the RMAN repository.
RMAN-06207: WARNING: 20036 objects could not be deleted for DISK channel(s) due
RMAN-06208:          to mismatched status.  Use CROSSCHECK command to fix status
RMAN-06210: List of Mismatched objects
RMAN-06211: ==========================
RMAN-06212:   Object Type   Filename/Handle
RMAN-06213: --------------- ---------------------------------------------------
RMAN-06214: Backup Piece    /rman-backup/sales-db/full_52ob26cd_149666_1
RMAN-06214: Backup Piece    /rman-backup/sales-db/full_50ob26cd_149664_1
As you see from the following output, crosscheck backup finds few records that are expired, and marking them as EXPIRED status in the catalog.
RMAN> CROSSCHECK BACKUP;

allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=453 devtype=DISK
allocated channel: ORA_DISK_2
channel ORA_DISK_2: sid=435 devtype=DISK
crosschecked backup piece: found to be 'AVAILABLE'
backup piece handle=/rman-backup/sales-db/full_4cpqjjca_196748_1 recid=217396 stamp=866766218
crosschecked backup piece: found to be 'AVAILABLE'
backup piece handle=/rman-backup/sales-db/full_4epqjjca_196750_1 recid=217397 stamp=866766218
..
..
crosschecked backup piece: found to be 'EXPIRED'
backup piece handle=/rman-backup/sales-db/ctl_c-863661937-20141013-19 recid=211590 stamp=860879752
crosschecked backup piece: found to be 'EXPIRED'
backup piece handle=/rman-backup/sales-db/ctl_c-863661937-20141013-1a recid=211591 stamp=860879769
Crosschecked 1253 objects

3. View Backups Before Delete Expired

As you see from the output below, there are several records in the RMAN catalog that are marked as EXPIRED.
RMAN> LIST BACKUP SUMMARY;

List of Backups
===============
Key     TY LV S Device Type Completion Time #Pieces #Copies Compressed Tag
------- -- -- - ----------- --------------- ------- ------- ---------- ---
728642  B  A  X DISK        29-SEP-14       1       1       YES        DBARCHLOGS_20140929
728643  B  A  X DISK        29-SEP-14       1       1       YES        DBARCHLOGS_20140929
..
..
819598  B  A  A DISK        29-DEC-14       1       1       YES        DBARCHLOGS_20141229
819607  B  F  A DISK        29-DEC-14       1       1       NO         TAG20141229T161328
Note: The 4th column which has the title of “S” is Status column. The value “X” indicates EXPIRED status. The value “A” indicates AVAILABLE status.

4. Delete Expired RMAN Catalog Entries

The following will delete all the records that are in RMAN catalog which are marked as EXPIRED. In other words, this deletes the expired records that don’t have the corresponding physical RMAN backup file in the filesystem.
RMAN> DELETE EXPIRED BACKUP;

using channel ORA_DISK_1
using channel ORA_DISK_2
List of Backup Pieces
BP Key  BS Key  Pc# Cp# Status      Device Type Piece Name
------- ------- --- --- ----------- ----------- ----------
728642  728712  1   1   EXPIRED     DISK        /rman-backup/sales-db/full_52ob26cd_149666_1
728643  728713  1   1   EXPIRED     DISK        /rman-backup/sales-db/full_50ob26cd_149664_1
..
..
Do you really want to delete the above objects (enter YES or NO)?
As shown above, before deleting it will ask for a confirmation. Say “YES” to the above, which will delete those files as shown below.
backup piece handle=/rman-backup/sales-db/full_52ob26cd_149666_1 RECID=591 STAMP=840751343
deleted backup piece
backup piece handle=/rman-backup/sales-db/full_50ob26cd_149664_1 RECID=592 STAMP=840752380
deleted backup piece
..
Deleted 107 EXPIRED objects

5. View Backups After Delete Expired

After deleting the expired entries, view the catalog to make sure it contains only the active available RMAN backup records.
RMAN> LIST BACKUP SUMMARY;

List of Backups
===============
Key     TY LV S Device Type Completion Time #Pieces #Copies Compressed Tag
------- -- -- - ----------- --------------- ------- ------- ---------- ---
809652  B  F  A DISK        20-DEC-14       1       1       YES        DBFULLFILE_20141220
809653  B  F  A DISK        20-DEC-14       1       1       YES        DBFULLFILE_20141220
809654  B  F  A DISK        20-DEC-14       1       1       YES        DBFULLFILE_20141220
809655  B  F  A DISK        20-DEC-14       1       1       YES        DBFULLFILE_20141220
809656  B  F  A DISK        20-DEC-14       1       1       YES        DBFULLFILE_20141220
..
..
819598  B  A  A DISK        29-DEC-14       1       1       YES        DBARCHLOGS_20141229
819607  B  F  A DISK        29-DEC-14       1       1       NO         TAG20141229T161328
As you see from the Status column (4th column), we see only values “A”, which indicates that all the records in the RMAN catalog are in AVAILABLE status now.

How Install and Configure OpenLDAP on CentOS / RHEL Linux

OpenLDAPLDAP stands for Lightweight Directory Access Protocol.
LDAP is a solution to access centrally stored information over network. This centrally stored information is organized in a directory that follows X.500 standard.
The information is stored and organized in a hierarchical manner and the advantage of this approach is that the information can be grouped into containers and clients can access these containers whenever needed.
The OpenLDAP hierarchy is almost similar to the DNS hierarchy.

The following are the two most commonly used objects in OpenLDAP:
  1. cn (common name) – This refers to the leaf entries, which are end objects (for example: users and groups)
  2. dc (domain component) – This refers to one of the container entries in the LDAP hierarchy. If in a setup the LDAP hierarchy is mapped to a DNS hierarchy, typically all DNS domains are referred to as DC objects.
For example, if there is user in the hierarchy sam.jamesdalicha.com, the fully distinguished name of this user is referred as cn=sam, dc=jamesdalicha, dc=com. If you noticed in the FDN (fully distinguished name), a comma is used a separator and not a dot, which is common in DNS.
By using the different LDAP entry types, you can setup a hierarchical directory structure. This is the reason why openLDAP is so widely used. You can easily build an openLDAP hierarchy where objects in the other locations are easily referred to without storing them on local servers. This makes OpenLDAP a lightweight directory, especially when compared to other directory servers such as Microsoft’s Active directory.
Now lets see how to setup a single instance of an LDAP server that can be used by multiple clients in your network for authentication.

Install OpenLDAP Packages

On CentOS and RedHat, use yum install as shown below, to install the openldap related packages.
yum install -y openldap openldap-clients openldap-servers
You should install the following three packages:
  1. openldap-servers – This is the main LDAP server
  2. openldap-clients – This contains all required LDAP client utilities
  3. openldap – This packages contains the LDAP support libraries

LDAP Config Files

  • config.ldif – The LDAP default configuration is stored under a file in /etc/openldap/slapd.d/cn=config.ldif that is created in the LDIF format. This is the LDAP Input Format (LDIF), a specific format that allows you to enter information in to the LDAP directory.
  • olcDatabase{2}bdb.ldif – You can also modify the settings like number of connections the server can support, timeouts and other database settings under the file /etc/openldap/slapd.d/cn=config/olcDatabase{2}bdb.ldif. This is the file that also contains the parameters like LDAP root user and the base DN.

Create olcRootDN Account as Admin

It is always recommended to create a dedicated user account first with the full permissions to change information on the LDAP database.
Modify the olcDatabase={2}bdb.ldif file, and change the olcRootDN entry. The following is the default entry.
# grep olcRootDN /etc/openldap/slapd.d/cn=config/olcDatabase={2}bdb.ldif
olcRootDN: cn=Manager,dc=my-domain,dc=com
Change the above line to an admin user. In this example, user “ramesh” will be the olcRootDN.
olcRootDN: cn=ramesh,dc=jamesdalicha,dc=com

Create olcRootPW Root Password

Now use slappasswd command to create a hash for the root password you want to use. Once the password is generated, open the cn=config.ldif file, include the olcRootPW parameter, and copy the hashed password as shown below.
Execute the following command and specify a password. This will generate the hash for the given password.
# slappasswd
New password: SecretLDAPRootPass2015
Re-enter new password: SecretLDAPRootPass2015
{SSHA}1pgok6qWn24lpBkVreTDboTr81rg4QC6
Take the hash output of the above command and add it to the oclRootPW parameter in the config.ldif file as shown below.
# vi /etc/openldap/slapd.d/cn=config.ldif
olcRootPW: {SSHA}1pgok6qWn24lpBkVreTDboTr81rg4QC6

Create olcSuffix Domain Name

Now setup the olcSuffix and to set the domain that you want. Simply modify the line that starts with olcSuffix in the file olcDatabase={2}bdb.ldif as shown below.
# vi /etc/openldap/slapd.d/cn=config/olcDatabase={2}bdb.ldif
olcSuffix: dc=jamesdalicha,dc=com

Verify The Configuration Files

After you save the configuration, use the below commands to update the LDAP master database
updatedb
Use slaptest command to verify the configuration file as shown below. This should display “testing succeeded” message as shown below.
# slaptest -u
config file testing succeeded
You might get the following messages during the above command, which you can ignore for now.
54a39508 ldif_read_file: checksum error on "/etc/openldap/slapd.d/cn=config/olcDatabase={1}monitor.ldif"
54a39508 ldif_read_file: checksum error on "/etc/openldap/slapd.d/cn=config/olcDatabase={2}bdb.ldif"

Start the LDAP Server

Start the ldap server as shown below.
# service slapd start
Checking configuration files for slapd: [WARNING]
config file testing succeeded
Starting slapd:                         [  OK  ]

Verify the LDAP Search

To verify the ldap server is configured successfully, you can use the below command and verify that the domain entry is present.
# ldapsearch -x -b "dc=jamesdalicha,dc=com"
# extended LDIF
#
# LDAPv3
# base <dc=jamesdalicha,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# search result
search: 2
result: 32 No such object
# numResponses: 1

Base LDAP Structure in base.ldif

The use of OU (organizational unit) objects can help you in providing additional structure to the LDAP database. If you are planning on adding in different types of entries, such as users, groups, computers, printers and more to the LDAP directory, it makes it easier to put every entry type into its own container.
To create these OU’s, you can create an initial LDIF file as shown in the below example. In this example, this file allows you to create the base container which is dc=jamesdalicha,dc=com and it creates two organizational units with the names users and groups in that container.
# cat base.ldif
dn: dc=jamesdalicha,dc=com
objectClass: dcObject
objectClass: organization
o: jamesdalicha.com
dc: jamesdalicha
dn: ou=users,dc=jamesdalicha,dc=com
objectClass: organizationalUnit
objectClass: top
ou: users
dn: ou=groups,dc=jamesdalicha,dc=com
objectClass: organizationalUnit
objectClass: top
ou: groups

Import Base Structure Using ldapadd

Now we can import the base structure in to the LDAP directory using the ldapadd command as shown below.
# ldapadd -x -W -D "cn=ramesh,dc=jamesdalicha,dc=com" -f base.ldif
Enter LDAP Password:
adding new entry "dc=jamesdalicha,dc=com"
adding new entry "ou=users,dc=jamesdalicha,dc=com"
adding new entry "ou=groups,dc=jamesdalicha,dc=com"

Verify the Base Structure using ldapsearch

To verify the OUs are successfully created, use the following ldapsearch command.
# ldapsearch -x -W -D "cn=ramesh,dc=jamesdalicha,dc=com" -b "dc=jamesdalicha,dc=com" "(objectclass=*)"
Enter LDAP Password:
The output of the above command will display all the objects in the LDAP directory structure.
# extended LDIF
#
# LDAPv3
# base <dc=jamesdalicha,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# jamesdalicha.com
dn: dc=jamesdalicha,dc=com
objectClass: dcObject
objectClass: organization
o: jamesdalicha.com
dc: jamesdalicha
# users, jamesdalicha.com
dn: ou=users,dc=jamesdalicha,dc=com
objectClass: organizationalUnit
objectClass: top
ou: users
# groups, jamesdalicha.com
dn: ou=groups,dc=jamesdalicha,dc=com
objectClass: organizationalUnit
objectClass: top
ou: groups
# search result
search: 2
result: 0 Success
# numResponses: 4
# numEntries: 3
In the next OpenLDAP article, we’ll explain how to add new users and groups to the LDAP Directory.