From b8d65761b33728dc1fe8bc12777dd83e07fa2ba7 Mon Sep 17 00:00:00 2001 From: Reiner Hauser <Reiner.Hauser@cern.ch> Date: Wed, 4 Nov 2015 14:46:41 +0000 Subject: [PATCH] Allow to dynamically allocate a unique multicast address. The OKS value of the multicast address is allowed to be * instead of a real IP address. In this case the following procedure is used to allocate a unique MC address across all partitions. The network/netmask part of the string should still be defined, e.g. */10.149.0.0/255.255.0.0. The allocate_multicast_address() method of NameService should be called by a single application in the partition (typically the HLT supervisor). It will first iterate over all existing IS objects of type 'MultiCastGroup' in the RunParams IS server of the initial partition. If it finds an entry which matches its own partition, the multicast address from this object will be used. This means that MC groups will be stable as long as the initial partition is up and running. If the method does not find an entry, it will try to insert a new object which contains the multicast address as a string in its name. This guarantees the uniqueness of the object and therefore address. The method will loop over a range of pre-defined multicast address until insertion succeeds. If the range is exhausted an exception CannotAllocateMulticast is thrown. As a final step either the static or dynamically allocated address and network are published in the local partition in all DFConfig IS servers as an object with the fixed name 'MultiCastAddress'. The lookup_multicast_address() method simply looks up the latter object in its appropriate DFConfig IS server and returns it. Note that the IS object contains both the partition name and the network part, but the latter should always be taken straight from OKS for purposes of configuration. The IS object simply contains it as auxilliary information. --- asyncmsg/NameService.h | 37 +++++++++++- schema/MsgInfo_is.schema.xml | 17 ++++-- src/MultiCastGroup.h | 105 +++++++++++++++++++++++++++++++++++ src/MultiCastGroupNamed.h | 105 +++++++++++++++++++++++++++++++++++ src/NameService.cxx | 84 ++++++++++++++++++++++++++++ 5 files changed, 342 insertions(+), 6 deletions(-) create mode 100644 src/MultiCastGroup.h create mode 100644 src/MultiCastGroupNamed.h diff --git a/asyncmsg/NameService.h b/asyncmsg/NameService.h index 26fbabb..170abbb 100644 --- a/asyncmsg/NameService.h +++ b/asyncmsg/NameService.h @@ -41,6 +41,13 @@ namespace daq { ((std::string)m_name) ) + ERS_DECLARE_ISSUE_BASE(asyncmsg, + CannotAllocateMulticast, + Issue, + "Cannot allocate multicast address", + ERS_EMPTY, + ERS_EMPTY) + namespace asyncmsg { @@ -106,16 +113,42 @@ namespace daq { /** \brief Resolve a single specific application. * - * \param[in] Resolves exactly one specific remote applications. + * \param[in] name Resolves exactly one specific remote applications with this name. * * \throws daq::asyncmsg::CannotResolve, IS exceptions */ boost::asio::ip::tcp::endpoint resolve(const std::string& name) const; - /** \brief A helper method to parse a string of the form network/netmask. + /** + * \brief Allocate the multicast address for the partition. + * + * \param[in] addr The string representation of the address; can be a full IP address or '*'. + * \param[in] network The string representation of the network to use for multicast. + * \returns The allocated multicast address. + * \throws daq::asyncmsg::CannotAllocateMulticast, daq::asyncmsg::InvalidISServer + * + * Only one application per partition should call this. If the 'addr' parameter contains + * a '*', the multicast address will be dynamically allocated in such a way that it + * is unique per installation and across partitions (e.g. in Point 1). + */ + std::string allocate_multicast_address(const std::string& addr, const std::string& network); + + /** \brief Find the multicast address for the partition. * + * \returns A string containing the IP addresses usable by boost::asio. + * + * \throws daq::asyncmsg::CannotResolve + * + * Applications who need the multicast address of the partition should call this with the + * 'addr' taken from OKS. If 'addr' is '*' the multicast address will be dynamically looked up. */ + std::string lookup_multicast_address(const std::string& addr) const; + + /** typedef for readability, a 2-tuple of IP addresses representing network/netmask. */ typedef std::tuple<boost::asio::ip::address,boost::asio::ip::address> Network; + + /** \brief A helper method to parse a string of the form network/netmask. + */ static Network parse_address_network(const std::string& network); /** \brief A helper method to find the local interface matching netmask. diff --git a/schema/MsgInfo_is.schema.xml b/schema/MsgInfo_is.schema.xml index 7876424..a91496c 100644 --- a/schema/MsgInfo_is.schema.xml +++ b/schema/MsgInfo_is.schema.xml @@ -9,7 +9,6 @@ <!ATTLIST info name CDATA #REQUIRED type CDATA #REQUIRED - num-of-includes CDATA #REQUIRED num-of-items CDATA #REQUIRED oks-format CDATA #FIXED "schema" oks-version CDATA #REQUIRED @@ -50,7 +49,6 @@ range CDATA "" format (dec|hex|oct) "dec" is-multi-value (yes|no) "no" - multi-value-implementation (list|vector) "list" init-value CDATA "" is-not-null (yes|no) "no" > @@ -64,7 +62,6 @@ is-composite (yes|no) #REQUIRED is-exclusive (yes|no) #REQUIRED is-dependent (yes|no) #REQUIRED - multi-value-implementation (list|vector) "list" > <!ELEMENT method (method-implementation*)> <!ATTLIST method @@ -81,7 +78,12 @@ <oks-schema> -<info name="" type="" num-of-includes="0" num-of-items="1" oks-format="schema" oks-version="oks-06-06-07 built "May 2 2013"" created-by="rhauser" created-on="msu-pc7.cern.ch" creation-time="20130502T084342" last-modified-by="rhauser" last-modified-on="msu-pc7.cern.ch" last-modification-time="20130502T084544"/> +<info name="" type="" num-of-items="2" oks-format="schema" oks-version="oks-06-09-02 built "Nov 2 2015"" created-by="rhauser" created-on="msu-pc7.cern.ch" creation-time="20130502T084342" last-modified-by="rhauser" last-modified-on="msu-pc7.cern.ch" last-modification-time="20151102T125421"/> + +<include> + <file path="is/is.xml"/> +</include> + <class name="MsgInfo"> <superclass name="Info"/> @@ -90,4 +92,11 @@ <attribute name="Port" description="Port number" type="u16" is-not-null="yes"/> </class> + <class name="MultiCastGroup"> + <superclass name="Info"/> + <attribute name="Partition" description="The name of the partition where this multicast group is used." type="string" is-not-null="yes"/> + <attribute name="MulticastAddress" description="The address of the multicast group. It must be in the 224.*.*.* range." type="string" range="224\.[0-9]+\.[0-9]+\.[0-9]+" init-value="224.200.100.100" is-not-null="yes"/> + <attribute name="MulticastNetwork" description="The network address where multicast packages are being sent. This determines the outgoing interface and should be the network part of the " type="string" range="[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?" init-value="10.149.0.0" is-not-null="yes"/> + </class> + </oks-schema> diff --git a/src/MultiCastGroup.h b/src/MultiCastGroup.h new file mode 100644 index 0000000..ea75556 --- /dev/null +++ b/src/MultiCastGroup.h @@ -0,0 +1,105 @@ +#ifndef MULTICASTGROUP_H +#define MULTICASTGROUP_H + +#include <is/info.h> + +#include <string> +#include <ostream> + + +// <<BeginUserCode>> + +// <<EndUserCode>> +/** + * + * @author generated by the IS tool + * @version 04/11/15 + */ + +class MultiCastGroup : public ISInfo { +public: + + /** + * The name of the partition where this multicast group is used. + */ + std::string Partition; + + /** + * The address of the multicast group. It must be in the 224.*.*.* range. + */ + std::string MulticastAddress; + + /** + * The network address where multicast packages are being sent. This determines the outgoing interface and should be the network part of the + */ + std::string MulticastNetwork; + + + static const ISType & type() { + static const ISType type_ = MultiCastGroup( ).ISInfo::type(); + return type_; + } + + virtual std::ostream & print( std::ostream & out ) const { + ISInfo::print( out ); + out << std::endl; + out << "Partition: " << Partition << "\t// The name of the partition where this multicast group is used." << std::endl; + out << "MulticastAddress: " << MulticastAddress << "\t// The address of the multicast group. It must be in the 224.*.*.* range." << std::endl; + out << "MulticastNetwork: " << MulticastNetwork << "\t// The network address where multicast packages are being sent. This determines the outgoing interface and should be the network part of the "; + return out; + } + + MultiCastGroup( ) + : ISInfo( "MultiCastGroup" ) + { + initialize(); + } + + ~MultiCastGroup(){ + +// <<BeginUserCode>> + +// <<EndUserCode>> + } + +protected: + MultiCastGroup( const std::string & type ) + : ISInfo( type ) + { + initialize(); + } + + void publishGuts( ISostream & out ){ + out << Partition << MulticastAddress << MulticastNetwork; + } + + void refreshGuts( ISistream & in ){ + in >> Partition >> MulticastAddress >> MulticastNetwork; + } + +private: + void initialize() + { + MulticastAddress = "224.200.100.100"; + MulticastNetwork = "10.149.0.0"; + +// <<BeginUserCode>> + +// <<EndUserCode>> + } + + +// <<BeginUserCode>> + +// <<EndUserCode>> +}; + +// <<BeginUserCode>> + +// <<EndUserCode>> +inline std::ostream & operator<<( std::ostream & out, const MultiCastGroup & info ) { + info.print( out ); + return out; +} + +#endif // MULTICASTGROUP_H diff --git a/src/MultiCastGroupNamed.h b/src/MultiCastGroupNamed.h new file mode 100644 index 0000000..300b238 --- /dev/null +++ b/src/MultiCastGroupNamed.h @@ -0,0 +1,105 @@ +#ifndef MULTICASTGROUPNAMED_H +#define MULTICASTGROUPNAMED_H + +#include <is/namedinfo.h> + +#include <string> +#include <ostream> + + +// <<BeginUserCode>> + +// <<EndUserCode>> +/** + * + * @author generated by the IS tool + * @version 04/11/15 + */ + +class MultiCastGroupNamed : public ISNamedInfo { +public: + + /** + * The name of the partition where this multicast group is used. + */ + std::string Partition; + + /** + * The address of the multicast group. It must be in the 224.*.*.* range. + */ + std::string MulticastAddress; + + /** + * The network address where multicast packages are being sent. This determines the outgoing interface and should be the network part of the + */ + std::string MulticastNetwork; + + + static const ISType & type() { + static const ISType type_ = MultiCastGroupNamed( IPCPartition(), "" ).ISInfo::type(); + return type_; + } + + virtual std::ostream & print( std::ostream & out ) const { + ISNamedInfo::print( out ); + out << std::endl; + out << "Partition: " << Partition << "\t// The name of the partition where this multicast group is used." << std::endl; + out << "MulticastAddress: " << MulticastAddress << "\t// The address of the multicast group. It must be in the 224.*.*.* range." << std::endl; + out << "MulticastNetwork: " << MulticastNetwork << "\t// The network address where multicast packages are being sent. This determines the outgoing interface and should be the network part of the "; + return out; + } + + MultiCastGroupNamed( const IPCPartition & partition, const std::string & name ) + : ISNamedInfo( partition, name, "MultiCastGroup" ) + { + initialize(); + } + + ~MultiCastGroupNamed(){ + +// <<BeginUserCode>> + +// <<EndUserCode>> + } + +protected: + MultiCastGroupNamed( const IPCPartition & partition, const std::string & name, const std::string & type ) + : ISNamedInfo( partition, name, type ) + { + initialize(); + } + + void publishGuts( ISostream & out ){ + out << Partition << MulticastAddress << MulticastNetwork; + } + + void refreshGuts( ISistream & in ){ + in >> Partition >> MulticastAddress >> MulticastNetwork; + } + +private: + void initialize() + { + MulticastAddress = "224.200.100.100"; + MulticastNetwork = "10.149.0.0"; + +// <<BeginUserCode>> + +// <<EndUserCode>> + } + + +// <<BeginUserCode>> + +// <<EndUserCode>> +}; + +// <<BeginUserCode>> + +// <<EndUserCode>> +inline std::ostream & operator<<( std::ostream & out, const MultiCastGroupNamed & info ) { + info.print( out ); + return out; +} + +#endif // MULTICASTGROUPNAMED_H diff --git a/src/NameService.cxx b/src/NameService.cxx index b0763e1..a1c1b3e 100644 --- a/src/NameService.cxx +++ b/src/NameService.cxx @@ -3,10 +3,13 @@ #include "transport/Interface.h" #include "MsgInfo.h" +#include "MultiCastGroup.h" +#include "MultiCastGroupNamed.h" #include "is/infostream.h" #include "is/infodictionary.h" #include "is/serveriterator.h" +#include "is/infoiterator.h" #include <algorithm> @@ -346,5 +349,86 @@ namespace daq { return boost::asio::ip::address(); } + std::string NameService::allocate_multicast_address(const std::string& addr, const std::string& network) + { + MultiCastGroup group; + ISInfoDictionary dict(m_partition); + + if(addr == "*") { + + // Find potential entry in initial partition + ISInfoIterator it(IPCPartition(), "RunParams", MultiCastGroup::type()); + bool found = false; + + while(it++) { + it.value(group); + if(group.Partition == m_partition.name()) { + // found our partition. + found = true; + break; + } + } + + if(!found) { + // allocate a new group. + + unsigned short index = 1; + std::string name_prefix = "RunParams.MultiCast-"; + + group.Partition = m_partition.name(); + group.MulticastAddress = std::string("224.100.100.") + boost::lexical_cast<std::string>(index); + group.MulticastNetwork = network; + + while(!found && index < 256) { + + try { + dict.insert(name_prefix + group.MulticastAddress, group); + found = true; + } catch(...) { + // try next + index++; + } + } + } + + if(!found) { + // A dynamic address was requested, but we could not allocate one... + throw CannotAllocateMulticast(ERS_HERE); + } + } + + ISServerIterator servers(m_partition, m_is_server + ".*"); + if(servers.entries() == 0) { + throw InvalidISServer(ERS_HERE,m_is_server); + } + while(servers++) { + // may throw + try { + dict.checkin(std::string(servers.name()) + ".MultiCastAddress", group); + } catch(ers::Issue& reason) { + throw InvalidISServer(ERS_HERE, servers.name(), reason); + } + } + + return group.MulticastAddress; + } + + std::string NameService::lookup_multicast_address(const std::string& addr) const + { + if(addr == "*") { + + MultiCastGroupNamed group(m_partition, m_is_server + ".MultiCastAddress"); + + try { + group.checkout(); + return group.MulticastAddress; + + } catch (ers::Issue& ex) { + throw CannotResolve(ERS_HERE, addr, ex); + } + } + + return addr; + } } } -- GitLab