def __init__(self): import comoonics.cluster from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo doc=comoonics.cluster.parseClusterConf(os.path.join(testpath, "cluster.conf")) self.clusterRepository = ClusterRepository(doc.documentElement,doc) self.clusterinfo = ClusterInfo(self.clusterRepository)
def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterNode, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository( os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster self.clusterInfo.helper.setSimOutput() self.nics = list() for node in self.clusterInfo.getNodes(): node.helper.output = self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic)
def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterNode, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository(os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster self.clusterInfo.helper.setSimOutput() self.nics=list() for node in self.clusterInfo.getNodes(): node.helper.output=self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic)
def __init__(self, query): from xml.dom.ext.reader import Sax2 from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo ClusterAssistantHelper.__init__(self, query) self.error=False # create Reader object try: reader = Sax2.Reader() _file = open("/etc/cluster/cluster.conf", "r") reader = Sax2.Reader() doc = reader.fromStream(_file) #create comclusterRepository Object clusterRepository = ClusterRepository(doc.documentElement, doc) #create comclusterinfo object self.clusterInfo = ClusterInfo(clusterRepository) except Exception, e: ComLog.getLogger(__logStrLevel__).error("Error parsing cluster.conf %s" %e) ComLog.errorTraceLog() self.error=True
class test_ClusterNode(baseClusterTestClass): """ Methods from RedhatClusterNode """ def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterNode, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository( os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster self.clusterInfo.helper.setSimOutput() self.nics = list() for node in self.clusterInfo.getNodes(): node.helper.output = self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic) def testGetname(self): _list = self.createNodeList("name") _list.reverse() for node in self.clusterInfo.getNodes(): self.assertEqual(node.getName(), _list.pop()) def testGetid(self): _list = self.createNodeList("id") _list.reverse() for node in self.clusterInfo.getNodes(): self.assertEqual(node.getId(), _list.pop()) def testGetvotes(self): _list = self.createNodeList("votes") _list.reverse() for node in self.clusterInfo.getNodes(): self.assertEqual(node.getVotes(), _list.pop()) """ Methods from ComoonicsClusterNode """ def testGetnic(self): pass def testRootvolume(self): self.assertRaises(IndexError, self.clusterInfo.getNodes()[0].getRootvolume) self.assertEqual(self.clusterInfo.getNodes()[1].getRootvolume(), self.nodeValues[1]["rootvolume"]) def testRootfs(self): self.assertRaises(IndexError, self.clusterInfo.getNodes()[0].getRootFs) i = 0 for node in self.clusterInfo.getNodes(): if i > 0: self.assertEqual(node.getRootFs(), self.nodeValues[i]["rootfs"]) # else: # self.assertRaises(IndexError, node.getRootFs) i = i + 1 def testGetmountopts(self): self.assertRaises(IndexError, self.clusterInfo.getNodes()[0].getMountopts) i = 0 for node in self.clusterInfo.getNodes(): if i > 0: self.assertEqual(node.getMountopts(), self.nodeValues[i]["mountopts"]) # else: # self.assertRaises(IndexError, node.getMountopts()) i = i + 1 def testGetsyslog(self): i = 0 for node in self.clusterInfo.getNodes(): self.assertEqual(node.getSyslog(), self.nodeValues[i]["syslog"]) i = i + 1 def testGetscsifailover(self): i = 0 for node in self.clusterInfo.getNodes(): self.assertEqual(node.getScsifailover(), self.nodeValues[i]["scsifailover"]) i = i + 1 def testGetnics(self): """ Does test if return value is list, list values are NodeNics and number of list values Does not check if NodeNics in list are correct """ for node in self.clusterInfo.getNodes(): _nics = node.getNics() # prepare predefined ordered list of nicnames to compare _nodenames = [] for i in range(len(self.nicValues)): if self.nicValues[i]["nodename"] == node.getName(): _nodenames.append(self.nicValues[i]["name"]) # test if return type of tested function is list # test if type of list values is ComoonicsClusterNodeNic self.assertEqual(type(_nics), type([])) for nic in _nics: self.assertEqual(type(nic).__name__, "ComoonicsClusterNodeNic") # test order of nics in list i = 0 for nic in _nics: self.assertEqual(nic.getName(), _nodenames[i]) i = i + 1 def testGetnonstatics(self): for node in self.clusterInfo.getNodes(): for attr in node.non_statics.keys(): self.assert_( getattr(node, attr) in ["1", "0"], "testGetnonstatics(node=%s, attr=%s)%s!=%s" % (node.getName(), attr, getattr(node, attr), ["1", "0"]))
class test_ClusterInfo(baseClusterTestClass): """ Unittests for Clustertools """ def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo import logging from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterInfo, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository(os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster ComLog.setLevel(logging.DEBUG) self.clusterInfo.helper.setSimOutput() self.nics=list() for node in self.clusterInfo.getNodes(): node.helper.output=self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic) def testGetnodes(self): """ Tests only correct type of list and content as well as length of list. Does not check if the nodes in the list are correct. """ _tmp = self.clusterInfo.getNodes() self.assertEqual(type(_tmp), type([])) self.assertEqual(len(_tmp), len(self.nodeValues)) for node in _tmp: self.assertEqual(str(type(node)), "<class 'comoonics.cluster.ComClusterNode.ComoonicsClusterNode'>") def testGetnodeidentifiers(self): self.assertEqual(self.clusterInfo.getNodeIdentifiers('name'), self.createNodeList("name")) _tmp1 = self.clusterInfo.getNodeIdentifiers('id') _tmp2 = self.createNodeList("id") _tmp1.sort() _tmp2.sort() self.assertEqual(_tmp1, _tmp2) # Methods from RedhatClusterinfo def testQueryvalue1(self): self.assertEqual(self.createNodeList("name"), self.clusterInfo.queryValue("/cluster/clusternodes/clusternode/@name")) def testQueryvalue2(self): self.assertEqual(len(self.createNodeList("name")), self.clusterInfo.queryValue("count(/cluster/clusternodes/clusternode/@name)")[0]) def testQueryxml(self): _tmp1 = "name" _tmp2 = "gfs-node1" _query='/cluster/clusternodes/clusternode[@'+_tmp1+'="'+_tmp2+'"]' node=self.clusterInfo.queryXml(_query)[0] self.assertEquals(node.getAttribute(_tmp1), _tmp2, "%s==%s->%s != %s->%s" %(_query, node.getAttribute("name"), node.getAttribute("value"), _tmp1, _tmp2)) def testQueryProperties(self): from comoonics.ComDataObject import DataObject import comoonics.XmlTools _tmp1 = "name" _tmp2 = "gfs-node1" _tmp3 = "name" _tmp4 = "eth1" result=[ "MASTER=yes", "SLAVE=no", "DELAY=0" ] result.sort() _query='/cluster/clusternodes/clusternode[@'+_tmp1+'="'+_tmp2+'"]/com_info/eth[@'+_tmp3+'="'+_tmp4+'"]' propertieselement=self.clusterInfo.queryXml(_query)[0] properties=DataObject(propertieselement).getProperties().list().split("\n") properties.sort() self.assertEquals(properties, result, "%s==%s != %s" %(_query, properties, result)) def testGetnode(self): """ Requires proper function of method getName() """ for nodename in self.createNodeList("name"): self.assertEqual(self.clusterInfo.getNode(nodename).getName(), nodename) def testGetname(self): #except first nic because it does not have an mac-address _tmp = self.nicValues[1:] for i in range(len(_tmp)): self.assertEqual(_tmp[i]["nodename"], str(self.clusterInfo.getNodeName(_tmp[i]["mac"]))) def testGetid(self): #except first nic because it does not have an mac-address _tmp = self.nicValues[1:] for i in range(len(_tmp)): self.assertEqual(_tmp[i]["nodeid"], str(self.clusterInfo.getNodeId(_tmp[i]["mac"]))) def testGetFailoverdomainnodes(self): for i in range(len(self.failoverdomainValues)): self.assertEqual(self.clusterInfo.getFailoverdomainNodes(self.failoverdomainValues[i]["name"]), self.failoverdomainValues[i]["members"]) def testGetFailoverdomainprefnode(self): for i in range(len(self.failoverdomainValues)-1): self.assertEqual(self.clusterInfo.getFailoverdomainPrefNode(self.failoverdomainValues[i]["name"]), self.failoverdomainValues[i]["prefnode"]) # self.assertRaises(NameError,self.clusterInfo.getFailoverdomainPrefNode, self.failoverdomainValues[2]["name"]) def testGetNodeids(self): _tmp1 = self.clusterInfo.getNodeIds() _tmp2 = self.createNodeList("id") _tmp1.sort() _tmp2.sort() self.assertEqual(_tmp1, _tmp2) # Methods from ComoonicsClusterinfo def testGetNic(self): #except first nic because it does not have an mac-address _tmp = self.nicValues[1:] for i in range(len(_tmp)): self.assertEqual(self.clusterInfo.getNic(_tmp[i]["mac"]), _tmp[i]["name"]) # def testGetNicFailure(self): # self.assertRaises(ClusterMacNotFoundException, self.clusterInfo.getNic,"murks") # Methods that deal with non static attributes def testName(self): self.assertEqual("clu_generix", self.clusterInfo.name) def testGeneration(self): self.assertEqual("900", self.clusterInfo.generation) def testQuorate(self): self.assertEqual("1", self.clusterInfo.quorum_quorate) def testQuorumGroupMember(self): self.assertEqual("1", self.clusterInfo.quorum_groupmember)
class test_ClusterNode(baseClusterTestClass): """ Methods from RedhatClusterNode """ def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterNode, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository(os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster self.clusterInfo.helper.setSimOutput() self.nics=list() for node in self.clusterInfo.getNodes(): node.helper.output=self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic) def testGetname(self): _list = self.createNodeList("name") _list.reverse() for node in self.clusterInfo.getNodes(): self.assertEqual(node.getName(), _list.pop()) def testGetid(self): _list = self.createNodeList("id") _list.reverse() for node in self.clusterInfo.getNodes(): self.assertEqual(node.getId(), _list.pop()) def testGetvotes(self): _list = self.createNodeList("votes") _list.reverse() for node in self.clusterInfo.getNodes(): self.assertEqual(node.getVotes(), _list.pop()) """ Methods from ComoonicsClusterNode """ def testGetnic(self): pass def testRootvolume(self): self.assertRaises(IndexError, self.clusterInfo.getNodes()[0].getRootvolume) self.assertEqual(self.clusterInfo.getNodes()[1].getRootvolume(), self.nodeValues[1]["rootvolume"]) def testRootfs(self): self.assertRaises(IndexError, self.clusterInfo.getNodes()[0].getRootFs) i = 0 for node in self.clusterInfo.getNodes(): if i>0: self.assertEqual(node.getRootFs(), self.nodeValues[i]["rootfs"]) # else: # self.assertRaises(IndexError, node.getRootFs) i = i + 1 def testGetmountopts(self): self.assertRaises(IndexError, self.clusterInfo.getNodes()[0].getMountopts) i = 0 for node in self.clusterInfo.getNodes(): if i>0: self.assertEqual(node.getMountopts(), self.nodeValues[i]["mountopts"]) # else: # self.assertRaises(IndexError, node.getMountopts()) i = i + 1 def testGetsyslog(self): i = 0 for node in self.clusterInfo.getNodes(): self.assertEqual(node.getSyslog(), self.nodeValues[i]["syslog"]) i = i + 1 def testGetscsifailover(self): i = 0 for node in self.clusterInfo.getNodes(): self.assertEqual(node.getScsifailover(), self.nodeValues[i]["scsifailover"]) i = i + 1 def testGetnics(self): """ Does test if return value is list, list values are NodeNics and number of list values Does not check if NodeNics in list are correct """ for node in self.clusterInfo.getNodes(): _nics = node.getNics() # prepare predefined ordered list of nicnames to compare _nodenames = [] for i in range(len(self.nicValues)): if self.nicValues[i]["nodename"] == node.getName(): _nodenames.append(self.nicValues[i]["name"]) # test if return type of tested function is list # test if type of list values is ComoonicsClusterNodeNic self.assertEqual(type(_nics), type([])) for nic in _nics: self.assertEqual(type(nic).__name__, "ComoonicsClusterNodeNic") # test order of nics in list i = 0 for nic in _nics: self.assertEqual(nic.getName(), _nodenames[i]) i = i + 1 def testGetnonstatics(self): for node in self.clusterInfo.getNodes(): for attr in node.non_statics.keys(): self.assert_(getattr(node, attr) in ["1", "0" ], "testGetnonstatics(node=%s, attr=%s)%s!=%s" %(node.getName(), attr, getattr(node, attr), ["1", "0"]))
class test_ClusterNodeNic(baseClusterTestClass): """ Methods from ComoonicsClusterNodeNic """ def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterNodeNic, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository( os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster import logging self.clusterInfo.helper.setSimOutput() self.nics = list() for node in self.clusterInfo.getNodes(): node.helper.output = self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic) def testGetname(self): i = 0 for nic in self.nics: self.assertEqual(nic.getName(), self.nicValues[i]["name"]) i = i + 1 def testGetmac(self): i = 0 for nic in self.nics: self.assertEqual(nic.getMac(), self.nicValues[i]["mac"]) i = i + 1 def testGetip(self): i = 0 for nic in self.nics: self.assertEqual(nic.getIP(), self.nicValues[i]["ip"]) i = i + 1 def testGetgateway(self): i = 0 for nic in self.nics: self.assertEqual(nic.getGateway(), self.nicValues[i]["gateway"]) i = i + 1 def testGetnetmask(self): i = 0 for nic in self.nics: self.assertEqual(nic.getNetmask(), self.nicValues[i]["netmask"]) i = i + 1 def testGetmaster(self): i = 0 for nic in self.nics: self.assertEqual(nic.getMaster(), self.nicValues[i]["master"]) i = i + 1 def testGetslave(self): i = 0 for nic in self.nics: self.assertEqual(nic.getSlave(), self.nicValues[i]["slave"]) i = i + 1
class test_ClusterNodeNic(baseClusterTestClass): """ Methods from ComoonicsClusterNodeNic """ def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterNodeNic, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository(os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster import logging self.clusterInfo.helper.setSimOutput() self.nics=list() for node in self.clusterInfo.getNodes(): node.helper.output=self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic) def testGetname(self): i = 0 for nic in self.nics: self.assertEqual(nic.getName(), self.nicValues[i]["name"]) i = i + 1 def testGetmac(self): i = 0 for nic in self.nics: self.assertEqual(nic.getMac(), self.nicValues[i]["mac"]) i = i + 1 def testGetip(self): i = 0 for nic in self.nics: self.assertEqual(nic.getIP(), self.nicValues[i]["ip"]) i = i + 1 def testGetgateway(self): i = 0 for nic in self.nics: self.assertEqual(nic.getGateway(), self.nicValues[i]["gateway"]) i = i + 1 def testGetnetmask(self): i = 0 for nic in self.nics: self.assertEqual(nic.getNetmask(), self.nicValues[i]["netmask"]) i = i + 1 def testGetmaster(self): i = 0 for nic in self.nics: self.assertEqual(nic.getMaster(), self.nicValues[i]["master"]) i = i + 1 def testGetslave(self): i = 0 for nic in self.nics: self.assertEqual(nic.getSlave(), self.nicValues[i]["slave"]) i = i + 1
class test_ClusterInfo(baseClusterTestClass): """ Unittests for Clustertools """ def init(self): import os.path from comoonics.cluster.ComClusterRepository import ClusterRepository from comoonics.cluster.ComClusterInfo import ClusterInfo import logging from comoonics import ComSystem ComSystem.setExecMode(ComSystem.SIMULATE) super(test_ClusterInfo, self).init() #create comclusterRepository Object self.clusterRepository = ClusterRepository( os.path.join(self._testpath, "cluster2.conf")) #create comclusterinfo object self.clusterInfo = ClusterInfo(self.clusterRepository) # setup the cashes for clustat for redhat cluster ComLog.setLevel(logging.DEBUG) self.clusterInfo.helper.setSimOutput() self.nics = list() for node in self.clusterInfo.getNodes(): node.helper.output = self.clusterInfo.helper.output for nic in node.getNics(): self.nics.append(nic) def testGetnodes(self): """ Tests only correct type of list and content as well as length of list. Does not check if the nodes in the list are correct. """ _tmp = self.clusterInfo.getNodes() self.assertEqual(type(_tmp), type([])) self.assertEqual(len(_tmp), len(self.nodeValues)) for node in _tmp: self.assertEqual( str(type(node)), "<class 'comoonics.cluster.ComClusterNode.ComoonicsClusterNode'>" ) def testGetnodeidentifiers(self): self.assertEqual(self.clusterInfo.getNodeIdentifiers('name'), self.createNodeList("name")) _tmp1 = self.clusterInfo.getNodeIdentifiers('id') _tmp2 = self.createNodeList("id") _tmp1.sort() _tmp2.sort() self.assertEqual(_tmp1, _tmp2) # Methods from RedhatClusterinfo def testQueryvalue1(self): self.assertEqual( self.createNodeList("name"), self.clusterInfo.queryValue( "/cluster/clusternodes/clusternode/@name")) def testQueryvalue2(self): self.assertEqual( len(self.createNodeList("name")), self.clusterInfo.queryValue( "count(/cluster/clusternodes/clusternode/@name)")[0]) def testQueryxml(self): _tmp1 = "name" _tmp2 = "gfs-node1" _query = '/cluster/clusternodes/clusternode[@' + _tmp1 + '="' + _tmp2 + '"]' node = self.clusterInfo.queryXml(_query)[0] self.assertEquals( node.getAttribute(_tmp1), _tmp2, "%s==%s->%s != %s->%s" % (_query, node.getAttribute("name"), node.getAttribute("value"), _tmp1, _tmp2)) def testQueryProperties(self): from comoonics.ComDataObject import DataObject import comoonics.XmlTools _tmp1 = "name" _tmp2 = "gfs-node1" _tmp3 = "name" _tmp4 = "eth1" result = ["MASTER=yes", "SLAVE=no", "DELAY=0"] result.sort() _query = '/cluster/clusternodes/clusternode[@' + _tmp1 + '="' + _tmp2 + '"]/com_info/eth[@' + _tmp3 + '="' + _tmp4 + '"]' propertieselement = self.clusterInfo.queryXml(_query)[0] properties = DataObject( propertieselement).getProperties().list().split("\n") properties.sort() self.assertEquals(properties, result, "%s==%s != %s" % (_query, properties, result)) def testGetnode(self): """ Requires proper function of method getName() """ for nodename in self.createNodeList("name"): self.assertEqual( self.clusterInfo.getNode(nodename).getName(), nodename) def testGetname(self): #except first nic because it does not have an mac-address _tmp = self.nicValues[1:] for i in range(len(_tmp)): self.assertEqual(_tmp[i]["nodename"], str(self.clusterInfo.getNodeName(_tmp[i]["mac"]))) def testGetid(self): #except first nic because it does not have an mac-address _tmp = self.nicValues[1:] for i in range(len(_tmp)): self.assertEqual(_tmp[i]["nodeid"], str(self.clusterInfo.getNodeId(_tmp[i]["mac"]))) def testGetFailoverdomainnodes(self): for i in range(len(self.failoverdomainValues)): self.assertEqual( self.clusterInfo.getFailoverdomainNodes( self.failoverdomainValues[i]["name"]), self.failoverdomainValues[i]["members"]) def testGetFailoverdomainprefnode(self): for i in range(len(self.failoverdomainValues) - 1): self.assertEqual( self.clusterInfo.getFailoverdomainPrefNode( self.failoverdomainValues[i]["name"]), self.failoverdomainValues[i]["prefnode"]) # self.assertRaises(NameError,self.clusterInfo.getFailoverdomainPrefNode, self.failoverdomainValues[2]["name"]) def testGetNodeids(self): _tmp1 = self.clusterInfo.getNodeIds() _tmp2 = self.createNodeList("id") _tmp1.sort() _tmp2.sort() self.assertEqual(_tmp1, _tmp2) # Methods from ComoonicsClusterinfo def testGetNic(self): #except first nic because it does not have an mac-address _tmp = self.nicValues[1:] for i in range(len(_tmp)): self.assertEqual(self.clusterInfo.getNic(_tmp[i]["mac"]), _tmp[i]["name"]) # def testGetNicFailure(self): # self.assertRaises(ClusterMacNotFoundException, self.clusterInfo.getNic,"murks") # Methods that deal with non static attributes def testName(self): self.assertEqual("clu_generix", self.clusterInfo.name) def testGeneration(self): self.assertEqual("900", self.clusterInfo.generation) def testQuorate(self): self.assertEqual("1", self.clusterInfo.quorum_quorate) def testQuorumGroupMember(self): self.assertEqual("1", self.clusterInfo.quorum_groupmember)