Navigating the OpenDaylight Feature Forest

Posted · 1 Comment

OpenDaylight is made up of multiple projects, each providing several features. This allows the user to load only the features needed for their use case, although presents a challenge — how can we get a bird’s eye view of all the features that make up a distribution? After spending many hours figuring out which features do what and how they are made up, we decided to provide a way of seeing them all in one place.

Naturally, this requires crawling OpenDaylight’s projects and extracting their features. But the first challenge we faced is: where do we start? How can we explore every feature in every project of OpenDaylight? There is an artifact in the integrations project called org.opendaylight.integration/features-integration which contains a feature file that lists all the feature repositories in the distribution. Each repository is an artifact containing another feature file, which itself may link to furhter repositories, and so on. The goal, then, is to search all the repositories from this file, collecting all the features they refer to, and then recursively search all the repositories that they link to.

Using the Eclipse Aether library, accessing artifacts in a Maven repository becomes easy. Well, maybe “easy” is an understatement: although its documentation is lacking in some respects, we found that the Aether library is best understood by studying the provided examples. We were able to parse the features of a feature file given its artifact ID using something along these lines:


type FileInfo = (List[Repository], List[Feature])

def parse(group: String, artifact: String): FileInfo = {

// Set up the Aether request
val repoSystem: RepositorySystem = newRepositorySystem()
val session: RepositorySystemSession = newSession(repoSystem)
val artifactToSearch = new DefaultArtifact(group, artifact, "features", "xml", latestVersion(group,artifact), null)
val artifactRequest = new ArtifactRequest()
artifactRequest.setArtifact(artifactToSearch)
// "repositories" need only contain one repository, the OpenDaylight central repo.
artifactRequest.setRepositories(repositories)

// Submit request and parse artifact
val artifactResult = repoSystem.resolveArtifact(session, artifactRequest)
val file = artifactResult.getArtifact().getFile()
val xml = loadAsXML(file)
val features = parseFeatures(xml)
val repos = parseRepos(xml)

// return
(repos, features)
}

Our implementation was done in Scala using the Play framework to generate and serve the webpage. The functional aspect of Scala lends itself really well to the recursive nature of this problem.

Now that we’ve implemented parsing an artifact into repositories and features, we can simply do a graph search starting at the root feature file while collecting all the features we encounter into a list. And of course, let’s make use of some of the functional features provided to us by a language like Scala:


def crawl(info: FileInfo, reposVisited: List[Repository]): List[Feature] { 
val (repos, features) = info 
val unvisited = repos diff reposVisited 
val parsed = unvisited.map(r => parse(r.group, r.artifact)) // return my features + the features returned by each of my repositories 
features ++ (parsed flatMap (crawl(_, reposVisited ++ unvisited))) }

// Crawl the root feature file and remove duplicates 
val allFeatures = crawl(parse(ROOT_REPO_GROUP, ROOT_REPO_ARTIFACT), Nil).distinct 

At its essence, the above is a depth-first search starting at the root feature file. It maintains a list of repositories that have already been visited so as to defend against cycles in the graph (otherwise, we would get trapped in an infinite loop!).

Finally, we are left off with a list of every feature in OpenDaylight. Alas, we emerge from the forest of features, into the open, into the daylight!

One Response to "Navigating the OpenDaylight Feature Forest"
  1. Great stuff. Will be super useful 🙂

Leave a Reply