protected override void Render(HtmlBlock.Block html) { Hamlet.TBODY <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > tbody = html .Table("#nodelabels").Thead().Tr().Th(".name", "Label Name").Th(".numOfActiveNMs" , "Num Of Active NMs").Th(".totalResource", "Total Resource").().().Tbody(); RMNodeLabelsManager nlm = rm.GetRMContext().GetNodeLabelManager(); foreach (NodeLabel info in nlm.PullRMNodeLabelsInfo()) { Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > row = tbody.Tr().Td(info.GetLabelName().IsEmpty() ? "<NO_LABEL>" : info.GetLabelName ()); int nActiveNMs = info.GetNumActiveNMs(); if (nActiveNMs > 0) { row = row.Td().A(Url("nodes", "?" + YarnWebParams.NodeLabel + "=" + info.GetLabelName ()), nActiveNMs.ToString()).(); } else { row = row.Td(nActiveNMs.ToString()); } row.Td(info.GetResource().ToString()).(); } tbody.().(); }
protected internal override void Render(HtmlBlock.Block html) { Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > table = html .Div(JQueryUI.InfoWrap).Table(JQueryUI.Info).Tr().Th().$class(JQueryUI.CTh).$colspan (2).(info.About()).().(); int i = 0; foreach (ResponseInfo.Item item in info) { Hamlet.TR <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > tr = table.Tr((++i % 2 != 0) ? JQueryUI.Odd : JQueryUI.Even).Th(item.key); string value = item.value.ToString(); if (item.url == null) { if (!item.isRaw) { Hamlet.TD <Hamlet.TR <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet > > > > td = tr.Td(); if (value.LastIndexOf('\n') > 0) { string[] lines = value.Split("\n"); Hamlet.DIV <Hamlet.TD <Hamlet.TR <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet > > > > > singleLineDiv; foreach (string line in lines) { singleLineDiv = td.Div(); singleLineDiv.(line); singleLineDiv.(); } } else { td.(value); } td.(); } else { tr.Td()._r(value).(); } } else { tr.Td().A(Url(item.url), value).(); } tr.(); } table.().(); }
protected override void Render(HtmlBlock.Block html) { if (job == null) { html.P().("Sorry, no counters for nonexistent", $(AMParams.JobId, "job")).(); return; } if (!$(AMParams.TaskId).IsEmpty() && task == null) { html.P().("Sorry, no counters for nonexistent", $(AMParams.TaskId, "task")).(); return; } string columnType = task == null ? "Task" : "Task Attempt"; Hamlet.TBODY <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > tbody = html.Div(JQueryUI.InfoWrap).Table("#singleCounter").Thead().Tr().Th(".ui-state-default" , columnType).Th(".ui-state-default", "Value").().().Tbody(); foreach (KeyValuePair <string, long> entry in values) { Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet > > > > row = tbody.Tr(); string id = entry.Key; string val = entry.Value.ToString(); if (task != null) { row.Td(id); row.Td().Br().$title(val).().(val).(); } else { row.Td().A(Url("singletaskcounter", entry.Key, $(AMParams.CounterGroup), $(AMParams .CounterName)), id).(); row.Td().Br().$title(val).().A(Url("singletaskcounter", entry.Key, $(AMParams.CounterGroup ), $(AMParams.CounterName)), val).(); } row.(); } tbody.().().(); }
protected override void Render(HtmlBlock.Block html) { if (!IsValidRequest()) { html.H2($(Title)); return; } TaskType type = null; string symbol = $(AMParams.TaskType); if (!symbol.IsEmpty()) { type = MRApps.TaskType(symbol); } else { type = app.GetTask().GetType(); } Hamlet.TR <Hamlet.THEAD <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > headRow = html.Table("#attempts").Thead().Tr(); headRow.Th(".id", "Attempt").Th(".state", "State").Th(".status", "Status").Th(".node" , "Node").Th(".logs", "Logs").Th(".tsh", "Start Time"); if (type == TaskType.Reduce) { headRow.Th("Shuffle Finish Time"); headRow.Th("Merge Finish Time"); } headRow.Th("Finish Time"); //Attempt if (type == TaskType.Reduce) { headRow.Th("Elapsed Time Shuffle"); //Attempt headRow.Th("Elapsed Time Merge"); //Attempt headRow.Th("Elapsed Time Reduce"); } //Attempt headRow.Th("Elapsed Time").Th(".note", "Note"); Hamlet.TBODY <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > tbody = headRow .().().Tbody(); // Write all the data into a JavaScript array of arrays for JQuery // DataTables to display StringBuilder attemptsTableData = new StringBuilder("[\n"); foreach (TaskAttempt attempt in GetTaskAttempts()) { TaskAttemptInfo ta = new TaskAttemptInfo(attempt, false); string taid = ta.GetId(); string nodeHttpAddr = ta.GetNode(); string containerIdString = ta.GetAssignedContainerIdStr(); string nodeIdString = attempt.GetAssignedContainerMgrAddress(); string nodeRackName = ta.GetRack(); long attemptStartTime = ta.GetStartTime(); long shuffleFinishTime = -1; long sortFinishTime = -1; long attemptFinishTime = ta.GetFinishTime(); long elapsedShuffleTime = -1; long elapsedSortTime = -1; long elapsedReduceTime = -1; if (type == TaskType.Reduce) { shuffleFinishTime = attempt.GetShuffleFinishTime(); sortFinishTime = attempt.GetSortFinishTime(); elapsedShuffleTime = Times.Elapsed(attemptStartTime, shuffleFinishTime, false); elapsedSortTime = Times.Elapsed(shuffleFinishTime, sortFinishTime, false); elapsedReduceTime = Times.Elapsed(sortFinishTime, attemptFinishTime, false); } long attemptElapsed = Times.Elapsed(attemptStartTime, attemptFinishTime, false); int sortId = attempt.GetID().GetId() + (attempt.GetID().GetTaskId().GetId() * 10000 ); attemptsTableData.Append("[\"").Append(sortId + " ").Append(taid).Append("\",\"") .Append(ta.GetState()).Append("\",\"").Append(StringEscapeUtils.EscapeJavaScript (StringEscapeUtils.EscapeHtml(ta.GetStatus()))).Append("\",\"").Append("<a class='nodelink' href='" + MRWebAppUtil.GetYARNWebappScheme() + nodeHttpAddr + "'>").Append(nodeRackName + "/" + nodeHttpAddr + "</a>\",\"").Append("<a class='logslink' href='").Append (Url("logs", nodeIdString, containerIdString, taid, app.GetJob().GetUserName())) .Append("'>logs</a>\",\"").Append(attemptStartTime).Append("\",\""); if (type == TaskType.Reduce) { attemptsTableData.Append(shuffleFinishTime).Append("\",\"").Append(sortFinishTime ).Append("\",\""); } attemptsTableData.Append(attemptFinishTime).Append("\",\""); if (type == TaskType.Reduce) { attemptsTableData.Append(elapsedShuffleTime).Append("\",\"").Append(elapsedSortTime ).Append("\",\"").Append(elapsedReduceTime).Append("\",\""); } attemptsTableData.Append(attemptElapsed).Append("\",\"").Append(StringEscapeUtils .EscapeJavaScript(StringEscapeUtils.EscapeHtml(ta.GetNote()))).Append("\"],\n"); } //Remove the last comma and close off the array of arrays if (attemptsTableData[attemptsTableData.Length - 2] == ',') { attemptsTableData.Delete(attemptsTableData.Length - 2, attemptsTableData.Length - 1); } attemptsTableData.Append("]"); html.Script().$type("text/javascript").("var attemptsTableData=" + attemptsTableData ).(); Hamlet.TR <Hamlet.TFOOT <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > footRow = tbody.().Tfoot().Tr(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("attempt_name" ).$value("Attempt").().().Th().Input("search_init").$type(HamletSpec.InputType.text ).$name("attempt_state").$value("State").().().Th().Input("search_init").$type(HamletSpec.InputType .text).$name("attempt_status").$value("Status").().().Th().Input("search_init"). $type(HamletSpec.InputType.text).$name("attempt_node").$value("Node").().().Th() .Input("search_init").$type(HamletSpec.InputType.text).$name("attempt_node").$value ("Logs").().().Th().Input("search_init").$type(HamletSpec.InputType.text).$name( "attempt_start_time").$value("Start Time").().(); if (type == TaskType.Reduce) { footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("shuffle_time" ).$value("Shuffle Time").().(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("merge_time" ).$value("Merge Time").().(); } footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("attempt_finish" ).$value("Finish Time").().(); if (type == TaskType.Reduce) { footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_shuffle_time" ).$value("Elapsed Shuffle Time").().(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_merge_time" ).$value("Elapsed Merge Time").().(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_reduce_time" ).$value("Elapsed Reduce Time").().(); } footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("attempt_elapsed" ).$value("Elapsed Time").().().Th().Input("search_init").$type(HamletSpec.InputType .text).$name("note").$value("Note").().(); footRow.().().(); }
/* * (non-Javadoc) * @see org.apache.hadoop.yarn.webapp.view.HtmlBlock#render(org.apache.hadoop.yarn.webapp.view.HtmlBlock.Block) */ protected override void Render(HtmlBlock.Block html) { if (app.GetJob() == null) { html.H2($(Title)); return; } TaskType type = null; string symbol = $(AMParams.TaskType); if (!symbol.IsEmpty()) { type = MRApps.TaskType(symbol); } Hamlet.THEAD <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > thead; if (type != null) { thead = html.Table("#" + app.GetJob().GetID() + type).$class("dt-tasks").Thead(); } else { thead = html.Table("#tasks").Thead(); } //Create the spanning row int attemptColSpan = type == TaskType.Reduce ? 8 : 3; thead.Tr().Th().$colspan(5).$class("ui-state-default").("Task").().Th().$colspan( attemptColSpan).$class("ui-state-default").("Successful Attempt").().(); Hamlet.TR <Hamlet.THEAD <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > theadRow = thead.Tr().Th("Name").Th("State").Th("Start Time").Th("Finish Time" ).Th("Elapsed Time").Th("Start Time"); //Attempt if (type == TaskType.Reduce) { theadRow.Th("Shuffle Finish Time"); //Attempt theadRow.Th("Merge Finish Time"); } //Attempt theadRow.Th("Finish Time"); //Attempt if (type == TaskType.Reduce) { theadRow.Th("Elapsed Time Shuffle"); //Attempt theadRow.Th("Elapsed Time Merge"); //Attempt theadRow.Th("Elapsed Time Reduce"); } //Attempt theadRow.Th("Elapsed Time"); //Attempt Hamlet.TBODY <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > tbody = theadRow .().().Tbody(); // Write all the data into a JavaScript array of arrays for JQuery // DataTables to display StringBuilder tasksTableData = new StringBuilder("[\n"); foreach (Task task in app.GetJob().GetTasks().Values) { if (type != null && task.GetType() != type) { continue; } TaskInfo info = new TaskInfo(task); string tid = info.GetId(); long startTime = info.GetStartTime(); long finishTime = info.GetFinishTime(); long elapsed = info.GetElapsedTime(); long attemptStartTime = -1; long shuffleFinishTime = -1; long sortFinishTime = -1; long attemptFinishTime = -1; long elapsedShuffleTime = -1; long elapsedSortTime = -1; long elapsedReduceTime = -1; long attemptElapsed = -1; TaskAttempt successful = info.GetSuccessful(); if (successful != null) { TaskAttemptInfo ta; if (type == TaskType.Reduce) { ReduceTaskAttemptInfo rta = new ReduceTaskAttemptInfo(successful, type); shuffleFinishTime = rta.GetShuffleFinishTime(); sortFinishTime = rta.GetMergeFinishTime(); elapsedShuffleTime = rta.GetElapsedShuffleTime(); elapsedSortTime = rta.GetElapsedMergeTime(); elapsedReduceTime = rta.GetElapsedReduceTime(); ta = rta; } else { ta = new TaskAttemptInfo(successful, type, false); } attemptStartTime = ta.GetStartTime(); attemptFinishTime = ta.GetFinishTime(); attemptElapsed = ta.GetElapsedTime(); } tasksTableData.Append("[\"").Append("<a href='" + Url("task", tid)).Append("'>"). Append(tid).Append("</a>\",\"").Append(info.GetState()).Append("\",\"").Append(startTime ).Append("\",\"").Append(finishTime).Append("\",\"").Append(elapsed).Append("\",\"" ).Append(attemptStartTime).Append("\",\""); if (type == TaskType.Reduce) { tasksTableData.Append(shuffleFinishTime).Append("\",\"").Append(sortFinishTime).Append ("\",\""); } tasksTableData.Append(attemptFinishTime).Append("\",\""); if (type == TaskType.Reduce) { tasksTableData.Append(elapsedShuffleTime).Append("\",\"").Append(elapsedSortTime) .Append("\",\"").Append(elapsedReduceTime).Append("\",\""); } tasksTableData.Append(attemptElapsed).Append("\"],\n"); } //Remove the last comma and close off the array of arrays if (tasksTableData[tasksTableData.Length - 2] == ',') { tasksTableData.Delete(tasksTableData.Length - 2, tasksTableData.Length - 1); } tasksTableData.Append("]"); html.Script().$type("text/javascript").("var tasksTableData=" + tasksTableData).( ); Hamlet.TR <Hamlet.TFOOT <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > footRow = tbody.().Tfoot().Tr(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("task"). $value("ID").().().Th().Input("search_init").$type(HamletSpec.InputType.text).$name ("state").$value("State").().().Th().Input("search_init").$type(HamletSpec.InputType .text).$name("start_time").$value("Start Time").().().Th().Input("search_init"). $type(HamletSpec.InputType.text).$name("finish_time").$value("Finish Time").().( ).Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_time" ).$value("Elapsed Time").().().Th().Input("search_init").$type(HamletSpec.InputType .text).$name("attempt_start_time").$value("Start Time").().(); if (type == TaskType.Reduce) { footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("shuffle_time" ).$value("Shuffle Time").().(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("merge_time" ).$value("Merge Time").().(); } footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("attempt_finish" ).$value("Finish Time").().(); if (type == TaskType.Reduce) { footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_shuffle_time" ).$value("Elapsed Shuffle Time").().(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_merge_time" ).$value("Elapsed Merge Time").().(); footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("elapsed_reduce_time" ).$value("Elapsed Reduce Time").().(); } footRow.Th().Input("search_init").$type(HamletSpec.InputType.text).$name("attempt_elapsed" ).$value("Elapsed Time").().(); footRow.().().(); }
protected override void Render(HtmlBlock.Block html) { if (job == null) { html.P().("Sorry, no counters for nonexistent", $(AMParams.JobId, "job")).(); return; } if (!$(AMParams.TaskId).IsEmpty() && task == null) { html.P().("Sorry, no counters for nonexistent", $(AMParams.TaskId, "task")).(); return; } if (total == null || total.GetGroupNames() == null || total.CountCounters() == 0) { string type = $(AMParams.TaskId); if (type == null || type.IsEmpty()) { type = $(AMParams.JobId, "the job"); } html.P().("Sorry it looks like ", type, " has no counters.").(); return; } string urlBase; string urlId; if (task != null) { urlBase = "singletaskcounter"; urlId = MRApps.ToString(task.GetID()); } else { urlBase = "singlejobcounter"; urlId = MRApps.ToString(job.GetID()); } int numGroups = 0; Hamlet.TBODY <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > tbody = html.Div(JQueryUI.InfoWrap).Table("#counters").Thead().Tr().Th(".group.ui-state-default" , "Counter Group").Th(".ui-state-default", "Counters").().().Tbody(); foreach (CounterGroup g in total) { CounterGroup mg = map == null ? null : map.GetGroup(g.GetName()); CounterGroup rg = reduce == null ? null : reduce.GetGroup(g.GetName()); ++numGroups; // This is mostly for demonstration :) Typically we'd introduced // a CounterGroup block to reduce the verbosity. OTOH, this // serves as an indicator of where we're in the tag hierarchy. Hamlet.TR <Hamlet.THEAD <Hamlet.TABLE <Hamlet.TD <Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > > > > > > groupHeadRow = tbody .Tr().Th().$title(g.GetName()).$class("ui-state-default").(FixGroupDisplayName(g .GetDisplayName())).().Td().$class(JQueryUI.CTable).Table(".dt-counters").$id(job .GetID() + "." + g.GetName()).Thead().Tr().Th(".name", "Name"); if (map != null) { groupHeadRow.Th("Map").Th("Reduce"); } // Ditto Hamlet.TBODY <Hamlet.TABLE <Hamlet.TD <Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > > > > > group = groupHeadRow.Th(map == null ? "Value" : "Total").().().Tbody(); foreach (Counter counter in g) { // Ditto Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Hamlet.TD <Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Hamlet.DIV <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > > > > > > groupRow = group .Tr(); if (task == null && mg == null && rg == null) { groupRow.Td().$title(counter.GetName()).(counter.GetDisplayName()).(); } else { groupRow.Td().$title(counter.GetName()).A(Url(urlBase, urlId, g.GetName(), counter .GetName()), counter.GetDisplayName()).(); } if (map != null) { Counter mc = mg == null ? null : mg.FindCounter(counter.GetName()); Counter rc = rg == null ? null : rg.FindCounter(counter.GetName()); groupRow.Td(mc == null ? "0" : string.Format("%,d", mc.GetValue())).Td(rc == null ? "0" : string.Format("%,d", rc.GetValue())); } groupRow.Td(string.Format("%,d", counter.GetValue())).(); } group.().().().(); } tbody.().().(); }
protected override void Render(HtmlBlock.Block html) { html.(typeof(MetricsOverviewTable)); ResourceScheduler sched = rm.GetResourceScheduler(); string type = $(YarnWebParams.NodeState); string labelFilter = $(YarnWebParams.NodeLabel, CommonNodeLabelsManager.Any).Trim (); Hamlet.TBODY <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > tbody = html .Table("#nodes").Thead().Tr().Th(".nodelabels", "Node Labels").Th(".rack", "Rack" ).Th(".state", "Node State").Th(".nodeaddress", "Node Address").Th(".nodehttpaddress" , "Node HTTP Address").Th(".lastHealthUpdate", "Last health-update").Th(".healthReport" , "Health-report").Th(".containers", "Containers").Th(".mem", "Mem Used").Th(".mem" , "Mem Avail").Th(".vcores", "VCores Used").Th(".vcores", "VCores Avail").Th(".nodeManagerVersion" , "Version").().().Tbody(); NodeState stateFilter = null; if (type != null && !type.IsEmpty()) { stateFilter = NodeState.ValueOf(StringUtils.ToUpperCase(type)); } ICollection <RMNode> rmNodes = this.rm.GetRMContext().GetRMNodes().Values; bool isInactive = false; if (stateFilter != null) { switch (stateFilter) { case NodeState.Decommissioned: case NodeState.Lost: case NodeState.Rebooted: { rmNodes = this.rm.GetRMContext().GetInactiveRMNodes().Values; isInactive = true; break; } default: { Log.Debug("Unexpected state filter for inactive RM node"); break; } } } foreach (RMNode ni in rmNodes) { if (stateFilter != null) { NodeState state = ni.GetState(); if (!stateFilter.Equals(state)) { continue; } } else { // No filter. User is asking for all nodes. Make sure you skip the // unhealthy nodes. if (ni.GetState() == NodeState.Unhealthy) { continue; } } // Besides state, we need to filter label as well. if (!labelFilter.Equals(RMNodeLabelsManager.Any)) { if (labelFilter.IsEmpty()) { // Empty label filter means only shows nodes without label if (!ni.GetNodeLabels().IsEmpty()) { continue; } } else { if (!ni.GetNodeLabels().Contains(labelFilter)) { // Only nodes have given label can show on web page. continue; } } } NodeInfo info = new NodeInfo(ni, sched); int usedMemory = (int)info.GetUsedMemory(); int availableMemory = (int)info.GetAvailableMemory(); Hamlet.TR <Hamlet.TBODY <Hamlet.TABLE <Org.Apache.Hadoop.Yarn.Webapp.Hamlet.Hamlet> > > row = tbody.Tr().Td(StringUtils.Join(",", info.GetNodeLabels())).Td(info.GetRack ()).Td(info.GetState()).Td(info.GetNodeId()); if (isInactive) { row.Td().("N/A").(); } else { string httpAddress = info.GetNodeHTTPAddress(); row.Td().A("//" + httpAddress, httpAddress).(); } row.Td().Br().$title(info.GetLastHealthUpdate().ToString()).().(Times.Format(info .GetLastHealthUpdate())).().Td(info.GetHealthReport()).Td(info.GetNumContainers( ).ToString()).Td().Br().$title(usedMemory.ToString()).().(StringUtils.ByteDesc(usedMemory * BytesInMb)).().Td().Br().$title(availableMemory.ToString()).().(StringUtils.ByteDesc (availableMemory * BytesInMb)).().Td(info.GetUsedVirtualCores().ToString()).Td(info .GetAvailableVirtualCores().ToString()).Td(ni.GetNodeManagerVersion()).(); } tbody.().(); }